import re
from datetime import datetime, timedelta, date, time as dt_time
from typing import List, Dict, Any, Optional, Tuple, Union, Iterator
import json
import logging
import urllib3
import jieba
import hashlib
import requests
from django.conf import settings
from django.core.cache import cache
from ..models import Event

# 禁用不安全请求的警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# 获取logger
logger = logging.getLogger('django')

class ScheduleAIService:
    """日程AI服务类"""

    def __init__(self):
        """初始化日程AI服务"""
        # 加载AI配置
        self.api_key = getattr(settings, 'AI_API_KEY', None)
        self.api_base = getattr(settings, 'AI_API_BASE', None)
        self.config = getattr(settings, 'AI_CONFIG', {})

        # 检查AI配置
        if self.api_key and self.api_base:
            logger.info(f"AI服务初始化成功，使用API: {self.api_base}")
        else:
            logger.warning("AI服务配置不完整，部分功能可能不可用")

        # 初始化缓存
        self.cache_timeout = 3600  # 缓存过期时间，默认1小时

        # 初始化正则表达式
        self._init_regex_patterns()

        # 加载jieba分词
        jieba.setLogLevel(logging.INFO)

        # 日志记录配置状态
        if hasattr(settings, 'AI_API_KEY') and settings.AI_API_KEY:
            logger.info(f"AI API已启用，使用模型: {settings.AI_CONFIG.get('model', 'moonshot-v1-32k')}")
        else:
            logger.warning("AI API未配置，将使用规则匹配模式")

        # 确保关键方法存在
        if not hasattr(self, '_extract_reminders'):
            # 未找到方法，提供一个默认实现
            logger.warning("未找到_extract_reminders方法，使用默认实现")
            self._extract_reminders = self._default_extract_reminders

        if not hasattr(self, '_extract_description'):
            # 未找到方法，提供一个默认实现
            logger.warning("未找到_extract_description方法，使用默认实现")
            self._extract_description = self._default_extract_description

        logger.info("ScheduleAIService初始化完成")

    def _call_ai_api(self, prompt: Union[str, List[Dict[str, str]]], temperature: float = 0.3) -> Union[str, Iterator[str]]:
        """调用AI API

        Args:
            prompt: 提示文本或消息列表
            temperature: 温度参数，控制随机性

        Returns:
            Union[str, Iterator[str]]: API返回的响应或响应流
        """
        try:
            if not hasattr(settings, 'AI_API_KEY') or not settings.AI_API_KEY:
                logger.warning("未配置AI_API_KEY，无法调用AI API")
                return ""

            if not hasattr(settings, 'AI_API_BASE') or not settings.AI_API_BASE:
                logger.warning("未配置AI_API_BASE，无法调用AI API")
                return ""

            headers = {
                "Content-Type": "application/json",
                "Authorization": f"Bearer {settings.AI_API_KEY}"
            }

            # 处理不同格式的提示词
            if isinstance(prompt, str):
                messages = [{"role": "user", "content": prompt}]
                prompt_length = len(prompt)
            else:
                messages = prompt
                prompt_length = sum(len(msg.get('content', '')) for msg in messages)

            data = {
                "model": settings.AI_CONFIG.get('model', 'moonshot-v1-32k'),
                "messages": messages,
                "temperature": temperature,
                "stream": True,  # 启用流式输出
                "max_tokens": settings.AI_CONFIG.get('max_tokens', 2000)
            }

            logger.info(f"调用AI API，prompt长度: {prompt_length}")

            # 构建API URL
            api_url = f"{settings.AI_API_BASE}/chat/completions"

            # 获取代理设置
            proxies = settings.AI_PROXY if hasattr(settings, 'AI_PROXY') else None

            # 使用异步会话发送流式请求
            with requests.Session() as session:
                # 配置会话以优化流式传输
                session.trust_env = False  # 不使用环境变量中的代理设置

                # 发送流式请求
                response = session.post(
                    api_url,
                    headers=headers,
                    json=data,
                    stream=True,  # 启用流式请求
                    timeout=settings.AI_CONFIG.get('timeout', 30),
                    proxies=proxies,
                    verify=False
                )

                if response.status_code != 200:
                    logger.error(f"AI API调用失败，状态码: {response.status_code}，响应: {response.text}")
                    return ""

                # 优化流式响应处理
                full_response = ""
                buffer = ""

                # 使用更高效的方式处理流式响应
                for chunk in response.iter_content(chunk_size=1024):
                    if chunk:
                        buffer += chunk.decode('utf-8')
                        lines = buffer.split('\n')
                        buffer = lines.pop()  # 保留最后一个可能不完整的行

                        for line in lines:
                            line = line.strip()
                            if line.startswith('data: '):
                                line = line[6:]  # 移除 'data: ' 前缀
                                if line == '[DONE]':
                                    continue

                                try:
                                    json_data = json.loads(line)
                                    if json_data['choices'][0]['delta'].get('content'):
                                        content = json_data['choices'][0]['delta']['content']
                                        full_response += content
                                        # 实时返回内容
                                        yield content
                                except json.JSONDecodeError:
                                    continue

                # 处理缓冲区中剩余的数据
                if buffer:
                    lines = buffer.split('\n')
                    for line in lines:
                        line = line.strip()
                        if line.startswith('data: '):
                            line = line[6:]  # 移除 'data: ' 前缀
                            if line == '[DONE]':
                                continue

                            try:
                                json_data = json.loads(line)
                                if json_data['choices'][0]['delta'].get('content'):
                                    content = json_data['choices'][0]['delta']['content']
                                    full_response += content
                                    # 实时返回内容
                                    yield content
                            except json.JSONDecodeError:
                                continue

                return full_response

        except Exception as e:
            logger.exception(f"调用AI API时出错: {str(e)}")
            return ""

    def _extract_schedules_with_ai(self, text: str, current_date: date) -> List[Dict[str, Any]]:
        """使用AI提取日程信息

        Args:
            text: 原始文本
            current_date: 当前日期

        Returns:
            List[Dict[str, Any]]: 提取的日程信息列表
        """
        try:
            # 构建提示词
            messages = self._build_schedule_prompt(text, current_date)

            # 调用AI API获取流式响应
            response_stream = self._call_ai_api(messages, temperature=0.2)
            if not response_stream:
                return []

            # 优化流式响应处理
            buffer = ""
            schedules = []
            json_pattern = re.compile(r'```json\s*([\s\S]*?)\s*```')

            # 使用更高效的方式处理流式响应
            for chunk in response_stream:
                buffer += chunk

                # 尝试从buffer中提取完整的JSON
                json_matches = json_pattern.findall(buffer)
                if json_matches:
                    for json_text in json_matches:
                        # 清理可能的非JSON字符
                        json_text = re.sub(r'^[^[{]*', '', json_text)
                        json_text = re.sub(r'[^}\]]*$', '', json_text)

                        try:
                            # 解析JSON
                            parsed_data = json.loads(json_text)

                            # 处理不同的JSON格式
                            if isinstance(parsed_data, list):
                                # 直接是日程列表
                                raw_schedules = parsed_data
                            elif isinstance(parsed_data, dict) and 'events' in parsed_data:
                                # 包含events字段的对象
                                raw_schedules = parsed_data.get('events', [])
                            elif isinstance(parsed_data, dict) and 'schedules' in parsed_data:
                                # 包含schedules字段的对象
                                raw_schedules = parsed_data.get('schedules', [])
                            else:
                                # 单个日程对象
                                raw_schedules = [parsed_data]

                            # 处理每个日程
                            for schedule in raw_schedules:
                                # 创建事件上下文
                                try:
                                    # 尝试提取关键词
                                    keywords = self._extract_keywords(schedule.get('description', ''))
                                except Exception as e:
                                    logger.warning(f"提取关键词失败: {str(e)}，使用备选方案")
                                    # 备选方案：简单分词
                                    description = schedule.get('description', '')
                                    if description:
                                        # 简单分词，取长度大于1的词
                                        words = [w for w in jieba.cut(description) if len(w) > 1]
                                        keywords = words[:5]  # 最多取5个词
                                    else:
                                        keywords = []

                                event_context = {
                                    'event_type': schedule.get('event_type', ''),
                                    'description': schedule.get('description', ''),
                                    'keywords': keywords
                                }

                                # 提取或补全日期
                                if not schedule.get('date'):
                                    # 使用上下文提取日期
                                    extracted_date = self._extract_date_with_context(
                                        text,
                                        current_date,
                                        event_context
                                    )
                                    if extracted_date:
                                        schedule['date'] = extracted_date.isoformat()
                                        schedule['is_date_ai_suggested'] = True

                                # 验证和纠正日期
                                if schedule.get('date'):
                                    try:
                                        date_obj = date.fromisoformat(schedule['date'])
                                        corrected_date = self._validate_and_correct_date(
                                            date_obj,
                                            schedule.get('description', ''),
                                            current_date
                                        )
                                        if corrected_date and corrected_date != date_obj:
                                            schedule['date'] = corrected_date.isoformat()
                                            schedule['date_corrected'] = True
                                    except (ValueError, TypeError) as e:
                                        logger.warning(f"日期格式错误: {schedule.get('date')}, {str(e)}")

                                # 添加到结果列表
                                schedules.append(schedule)

                            # 清空buffer，避免重复处理
                            buffer = ""
                            break
                        except json.JSONDecodeError as e:
                            logger.warning(f"JSON解析错误: {str(e)}, 继续收集更多数据")
                            continue

            return schedules

        except Exception as e:
            logger.exception(f"使用AI提取日程信息时出错: {str(e)}")
            return []

    def _build_schedule_prompt(self, text: str, current_date: date) -> str:
        """构建日程提取提示词

        Args:
            text: 包含日程信息的文本
            current_date: 当前日期

        Returns:
            str: 构建的提示词
        """
        try:
            # 首先检查是否有相对日期词（今天、明天、后天等）
            relative_date = None
            for word, days in self.date_patterns['date_words'].items():
                if word in text:
                    relative_date = current_date
                    logger.info(f"在AI提取中找到相对日期词 '{word}'，使用当前日期 {current_date}")
                    break

            # 检查"X天后"格式
            if not relative_date:
                days_later_match = re.search(self.date_patterns['days_later'], text)
                if days_later_match:
                    relative_date = current_date
                    logger.info(f"在AI提取中找到'X天后'格式，使用当前日期 {current_date}")

            # 检测是否包含考试科目相关内容
            is_exam_content = any(keyword in text for keyword in ["考试", "科目", "测试", "复习", "试卷"])

            # 构建系统提示词
            system_prompt = """你是一个专业的日程提取助手，擅长从文本中精确提取日程信息并以JSON格式返回。
你的任务是从用户提供的文本中提取所有独立的日程任务信息，特别是课程和考试信息。
你需要确保提取的信息准确、完整，并符合指定的格式要求。"""

            if is_exam_content:
                system_prompt += """
对于考试科目信息，你需要特别注意：
1. 科目名称必须准确、专业，如"数字逻辑电路"、"Python程序设计"等
2. 描述应包含核心知识点，如"数字电路基础知识"、"Python函数与类"等
3. 关键点应突出考试重点，如"逻辑门"、"循环结构"等
4. 时间信息必须精确到分钟，如"09:00-11:00"
5. 地点信息应尽可能详细，如"1-7教室"、"计算机楼203"等"""

            # 构建用户提示词
            user_prompt = f"""请从以下文本中提取所有独立的日程任务信息：

```
{text}
```

当前日期：{current_date.strftime('%Y-%m-%d')}
"""

            # 如果有相对日期，添加特别说明
            if relative_date:
                user_prompt += f"""
特别说明：文本中包含相对日期词（如"今天"、"明天"、"后天"、"X天后"等），这些日期是相对于当前日期（{current_date.strftime('%Y-%m-%d')}）计算的。
请直接使用我提供的当前日期作为基准，不要再次计算相对日期。
所有任务的日期应该是：{current_date.strftime('%Y-%m-%d')}
"""

            user_prompt += """
请仔细分析文本，注意以下几点：
1. 每个任务都应该被单独识别，不要将多个任务合并为一个
2. 如果文本中有多行，每行可能代表一个独立的任务
3. 如果文本中有明确的时间段（如"上午9点到10点"），这通常表示一个独立的任务
4. 对于模糊的时间表达（如"晚上"），请根据常识推断合理的时间范围
5. 如果文本中明确提到了日期（如"3月12日"），请使用这个日期，而不是当前日期
6. 如果文本中使用了相对日期（如"明天"、"后天"），请使用我提供的当前日期作为基准

请以JSON格式返回提取结果，每个任务包含以下字段：
- title: 任务标题，准确概括任务内容，对于考试科目应包含完整科目名称
- description: 任务详细描述，包含核心信息，对于考试科目应包含主要知识点
- date: 日期，格式为YYYY-MM-DD
- start_time: 开始时间，格式为HH:MM
- end_time: 结束时间，格式为HH:MM
- period: 时间段，可选值为morning(上午)、afternoon(下午)、evening(晚上)、allday(全天)
- importance: 重要性，可选值为low、medium、high
- location: 地点，如果有的话，应尽可能详细
- key_points: 关键点列表，对于考试科目应突出考试重点
- is_date_ai_suggested: 布尔值，表示日期是否由AI推断
- is_time_ai_suggested: 布尔值，表示时间是否由AI推断
- is_location_ai_suggested: 布尔值，表示地点是否由AI推断

请确保：
1. 标题准确完整，特别是科目名称必须专业规范
2. 描述内容丰富，包含核心信息
3. 关键点突出重点，便于用户快速把握
4. 根据任务类型推断合理的时间长度（如会议通常1-2小时，课程通常2-3小时）
5. 如果文本中没有明确提到地点，但可以从上下文推断，请填写并标记为AI推断
6. 返回的JSON必须是有效的格式，每个字段的值类型必须正确
"""

            if relative_date:
                user_prompt += f"""
7. 对于包含相对日期词的任务，日期必须是 {current_date.strftime('%Y-%m-%d')}
"""

            user_prompt += """
示例输出格式：
```json
[
  {
    "title": "数字逻辑电路",
    "description": "数字电路基础知识",
    "date": "2023-05-15",
    "start_time": "09:00",
    "end_time": "11:00",
    "period": "morning",
    "importance": "high",
    "location": "1-7教室",
    "key_points": ["逻辑门", "真值表"],
    "is_date_ai_suggested": false,
    "is_time_ai_suggested": false,
    "is_location_ai_suggested": true
  }
]
```

请只返回JSON格式的结果，不要包含其他解释性文字。确保每个字段的值都是有意义的，不要出现"科目：xxx"这样的格式，而是直接提取出科目名称。
"""

            # 构建完整提示词
            messages = [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt}
            ]

            return messages

        except Exception as e:
            logger.error(f"构建日程提取提示词时出错: {str(e)}")
            # 返回基本提示词
            return [
                {"role": "system", "content": "你是一个专业的日程提取助手，擅长从文本中提取日程信息并以JSON格式返回。"},
                {"role": "user", "content": f"请从以下文本中提取日程信息：\n\n{text}\n\n当前日期：{current_date.strftime('%Y-%m-%d')}"}
            ]

    def analyze_content(self, text: str, current_date_info: Dict = None) -> List[Dict[str, Any]]:
        """使用AI分析文本内容并提取日程信息

        Args:
            text: 要分析的文本内容
            current_date_info: 当前日期信息，可能包含年月日和星期

        Returns:
            List[Dict[str, Any]]: 提取的日程信息列表
        """
        try:
            # 记录原始输入
            logger.info(f"分析文本: {text[:100]}{'...' if len(text) > 100 else ''}")

            # 首先检查文本中是否有明确的日期
            explicit_date = None
            explicit_date_match = re.search(r'(\d{1,2})月(\d{1,2})日', text)
            if explicit_date_match:
                month = int(explicit_date_match.group(1))
                day = int(explicit_date_match.group(2))
                year = datetime.now().year  # 默认使用当前年份

                try:
                    explicit_date = date(year, month, day)
                    logger.info(f"从文本中提取到明确的日期: {explicit_date}")
                except ValueError:
                    logger.warning(f"无效的日期: {year}-{month}-{day}")

            # 如果提供了当前日期信息，使用它构建当前日期
            if current_date_info is not None:
                try:
                    year = int(current_date_info.get('year', datetime.now().year))
                    month = int(current_date_info.get('month', datetime.now().month))
                    day = int(current_date_info.get('day', datetime.now().day))
                    current_date = date(year, month, day)

                    # 记录后端生成的日期信息
                    logger.info(f"使用后端生成的日期信息: {year}年{month}月{day}日, Python星期={current_date.weekday()}")
                except (ValueError, TypeError, KeyError) as e:
                    logger.warning(f"无法从传入的日期信息构建日期: {str(e)}")
                    current_date = datetime.now().date()
                    logger.info(f"使用系统当前日期: {current_date}")
            else:
                current_date = datetime.now().date()
                logger.info(f"未提供日期信息，使用系统当前日期: {current_date}")

            # 检查是否有相对日期词（今天、明天、后天等）
            relative_date = None
            for word, days in self.date_patterns['date_words'].items():
                if word in text:
                    relative_date = current_date + timedelta(days=days)
                    logger.info(f"找到相对日期词 '{word}'，基于当前日期 {current_date} 计算得到: {relative_date}")
                    break

            # 检查"X天后"格式
            if not relative_date:
                days_later_match = re.search(self.date_patterns['days_later'], text)
                if days_later_match:
                    days = int(days_later_match.group(1))
                    if 1 <= days <= 30:  # 限制在30天内，以避免错误解析
                        relative_date = current_date + timedelta(days=days)
                        logger.info(f"找到'{days}天后'，基于当前日期 {current_date} 计算得到: {relative_date}")

            # 确定最终使用的日期：明确日期 > 相对日期 > 当前日期
            final_date = explicit_date or relative_date or current_date
            if final_date != current_date:
                logger.info(f"使用{'明确' if explicit_date else '相对'}日期: {final_date}")
                current_date = final_date

            # 检查缓存
            cache_key = f"schedule_analysis:{hashlib.md5(text.encode()).hexdigest()}"
            cached_result = self._get_cache(cache_key)
            if cached_result:
                logger.info("使用缓存的分析结果")
                return cached_result

            # 预处理文本
            processed_text = self._preprocess_text(text)
            logger.debug(f"预处理后的文本: {processed_text[:100]}{'...' if len(processed_text) > 100 else ''}")

            # 尝试使用AI提取日程
            schedules = []
            if hasattr(settings, 'AI_API_KEY') and settings.AI_API_KEY:
                logger.info("尝试使用AI提取日程")
                try:
                    # 直接调用AI API提取日程
                    ai_schedules = self._extract_schedules_with_ai(processed_text, current_date)
                    if ai_schedules:
                        logger.info(f"AI成功提取到 {len(ai_schedules)} 个日程")
                        for i, schedule in enumerate(ai_schedules):
                            # 确保日程使用正确的日期
                            if explicit_date or relative_date:
                                schedule['date'] = current_date.strftime('%Y-%m-%d')
                                schedule['is_date_ai_suggested'] = False
                            logger.info(f"日程 {i+1}: {schedule.get('title', '无标题')}, 日期: {schedule.get('date', '无日期')}, 时间: {schedule.get('start_time', '无开始时间')}-{schedule.get('end_time', '无结束时间')}")
                        schedules = ai_schedules
                    else:
                        logger.warning("AI未能提取到日程，将使用规则匹配")
                except Exception as e:
                    logger.exception(f"使用AI提取日程时出错: {e}")

            # 如果AI提取失败，使用规则匹配
            if not schedules:
                logger.info("使用规则匹配提取日程")
                try:
                    rule_schedules = self._extract_schedule_info(processed_text, current_date)
                    if rule_schedules:
                        logger.info(f"规则匹配成功提取到 {len(rule_schedules)} 个日程")
                        for i, schedule in enumerate(rule_schedules):
                            # 确保日程使用正确的日期
                            if explicit_date or relative_date:
                                schedule['date'] = current_date.strftime('%Y-%m-%d')
                                schedule['is_date_ai_suggested'] = False
                            logger.info(f"日程 {i+1}: {schedule.get('title', '无标题')}, 日期: {schedule.get('date', '无日期')}, 时间: {schedule.get('start_time', '无开始时间')}-{schedule.get('end_time', '无结束时间')}")
                        schedules = rule_schedules
                    else:
                        logger.warning("规则匹配未能提取到日程")
                except Exception as e:
                    logger.exception(f"使用规则匹配提取日程时出错: {e}")

            # 找到不是AI提取的情况下进行简单处理
            if not schedules:
                logger.warning("AI分析失败，使用基本规则创建日程")
                basic_schedule = {
                    'title': '未命名任务',
                    'description': '请查看详情',
                    'date': current_date.strftime('%Y-%m-%d'),
                    'start_time': '09:00',
                    'end_time': '10:00',
                    'period': 'morning',
                    'importance': 'medium',
                    'location': '',
                    'key_points': [],
                    'is_date_ai_suggested': True,
                    'is_time_ai_suggested': True,
                    'is_location_ai_suggested': False
                }
                schedules = [basic_schedule]

            # 缓存结果
            if schedules:
                self._set_cache(cache_key, schedules)
                logger.info(f"缓存分析结果，共 {len(schedules)} 个日程")

            return schedules

        except Exception as e:
            logger.exception(f"分析文本内容时出错: {str(e)}")
            return []

    def _preprocess_text(self, text: str) -> str:
        """预处理文本，移除可能的干扰信息，优化多任务识别

        Args:
            text: 原始文本

        Returns:
            str: 处理后的文本
        """
        # 记录原始文本
        logger.debug(f"预处理前的文本: {text}")

        # 移除可能的日期前缀
        date_prefix_pattern = r'^\d{1,2}月\d{1,2}日\s*'
        text = re.sub(date_prefix_pattern, '', text)

        # 移除可能的任务前缀
        task_prefix_patterns = [
            r'^.*?(我需要|计划|安排|日程|任务)[：:]\s*',
            r'^.*?完成以下任务[：:]\s*',
            r'^.*?我需要完成以下任务[：:]\s*',
            r'^.*?今天需要完成[：:]\s*',
            r'^.*?明天需要完成[：:]\s*'
        ]

        for pattern in task_prefix_patterns:
            text = re.sub(pattern, '', text)

        # 处理换行符，确保每个任务在单独的行
        text = text.replace('\r\n', '\n').replace('\r', '\n')

        # 如果文本中有明确的编号（如1. 2. 3.），确保它们在单独的行
        text = re.sub(r'(\d+\.\s*)', r'\n\1', text)

        # 如果文本中有明确的时间段，确保它们在单独的行
        time_patterns = [
            r'(上午|下午|晚上|中午)(\d{1,2})[点:：](\d{1,2})?',
            r'(\d{1,2})[点:：](\d{1,2})?到(\d{1,2})[点:：](\d{1,2})?',
            r'(\d{1,2})[点:：](\d{1,2})?[-~至](\d{1,2})[点:：](\d{1,2})?'
        ]

        for pattern in time_patterns:
            text = re.sub(f'([^\n])({pattern})', r'\1\n\2', text)

        # 移除多余的空白字符，但保留换行符
        text = re.sub(r'[ \t]+', ' ', text)

        # 移除可能的特殊字符
        text = re.sub(r'[""]', '"', text)
        text = re.sub(r"['']", "'", text)

        # 替换常见的时间表达简写
        text = re.sub(r'(\d+)[:：](\d+)', r'\1:\2', text)  # 统一时间分隔符为英文冒号
        text = re.sub(r'(\d+)点半', r'\1:30', text)  # 将"点半"转换为":30"

        # 确保文本不以换行符开始或结束
        text = text.strip()

        # 分割成多行，每行作为一个潜在的任务
        lines = text.split('\n')
        processed_lines = []

        for line in lines:
            line = line.strip()
            if line:  # 忽略空行
                processed_lines.append(line)

        # 重新组合文本，确保每个任务在单独的行
        processed_text = '\n'.join(processed_lines)

        logger.debug(f"预处理后的文本: {processed_text}")
        return processed_text

    def _get_cache(self, key: str) -> Optional[Any]:
        """从缓存中获取数据

        Args:
            key: 缓存键

        Returns:
            Optional[Any]: 缓存的数据，如果不存在则返回None
        """
        try:
            # 使用Django缓存
            return cache.get(key)
        except Exception as e:
            logger.error(f"获取缓存失败: {str(e)}")
            return None

    def _set_cache(self, key: str, value: Any, timeout: int = 3600) -> bool:
        """设置缓存数据

        Args:
            key: 缓存键
            value: 要缓存的数据
            timeout: 缓存超时时间（秒），默认1小时

        Returns:
            bool: 是否成功设置缓存
        """
        try:
            # 使用Django缓存
            cache.set(key, value, timeout=timeout)
            return True
        except Exception as e:
            logger.error(f"设置缓存失败: {str(e)}")
            return False

    def generate_schedule(self, user, text: str, date: date = None) -> List[Event]:
        """生成日程

        Args:
            user: 用户对象
            text: 日程文本
            date: 日程日期，默认为今天

        Returns:
            List[Event]: 创建的日程对象列表
        """
        try:
            if not text:
                logger.warning("内容为空，无法生成日程")
                return []

            # 使用缓存避免重复处理
            cache_key = f"schedule_cache_{hash(text)}"
            cached_result = self._get_cache(cache_key)
            if cached_result:
                logger.info("使用缓存的日程数据")
                return cached_result

            if date is None:
                date = datetime.now().date()

            # 提取日程信息
            schedule_infos = self._extract_schedule_info(text, date)
            created_events = []

            for info in schedule_infos:
                try:
                    # 获取日期，优先使用提取的日期，如果没有则使用传入的日期或当天
                    event_date = datetime.strptime(info['date'], '%Y-%m-%d').date() if 'date' in info else (date or datetime.now().date())

                    # 设置提醒时间（默认提前30分钟）
                    start_time = datetime.strptime(info['start_time'], '%H:%M').time()
                    reminder_datetime = datetime.combine(event_date, start_time) - timedelta(minutes=30)

                    # 创建日程对象
                    event = Event(
                        user=user,
                        title=info['title'],
                        description=info['description'],
                        date=event_date,
                        start_time=start_time,
                        end_time=datetime.strptime(info['end_time'], '%H:%M').time(),
                        period=info['period'],
                        importance=info['importance'],
                        status=info['status'],
                        reminder=info['reminder'],
                        reminder_time=reminder_datetime,
                        location=info.get('location', '')  # 添加地点信息
                    )

                    # 保存日程
                    event.save()
                    created_events.append(event)
                except Exception as e:
                    logger.error(f"创建日程对象失败: {str(e)}")
                    continue

            # 缓存创建的日程
            if created_events:
                self._set_cache(cache_key, created_events)

            return created_events

        except Exception as e:
            logger.error(f"生成日程失败: {str(e)}")
            return []

    def process_draft(self, user, draft_text: str, target_date: date = None) -> Dict[str, Any]:
        """处理草稿文本，生成日程

        Args:
            user: 用户对象
            draft_text: 草稿文本
            target_date: 目标日期，默认为今天

        Returns:
            Dict: 处理结果
        """
        try:
            events = self.generate_schedule(user, draft_text, target_date)

            return {
                'status': 'success',
                'message': f'成功创建 {len(events)} 个日程',
                'data': {
                    'event_count': len(events),
                    'events': [{
                        'id': event.id,
                        'title': event.title,
                        'start_time': event.start_time.strftime('%H:%M'),
                        'end_time': event.end_time.strftime('%H:%M'),
                        'description': event.description,
                        'importance': event.importance,
                        'location': event.location,
                        'period': event.period
                    } for event in events]
                }
            }

        except Exception as e:
            logger.error(f"处理草稿失败: {str(e)}")
            return {
                'status': 'error',
                'message': f'创建日程失败: {str(e)}',
                'data': None
            }

    def _complete_missing_date(self, schedule_info, text, current_date: date = None):
        """补全缺失的日期信息

        Args:
            schedule_info: 日程信息字典
            text: 原始文本
            current_date: 当前日期，如果提供则使用此日期作为基准

        Returns:
            Dict: 更新后的日程信息
        """
        # 如果已有日期，直接返回，但始终标记为AI建议，需要用户确认
        if 'date' in schedule_info and schedule_info['date']:
            # 无论是否从文本中提取到日期，都标记为需要用户确认
            schedule_info['is_date_ai_suggested'] = True
            logger.info(f"日期 {schedule_info['date']} 标记为需要用户确认")
            return schedule_info

        # 基准日期（今天）
        # 如果提供了current_date，则使用它作为基准日期
        if current_date:
            today = current_date
            logger.info(f"使用提供的当前日期: {today}")
        else:
            # 否则使用硬编码的2025年日期
            today_real = datetime.now().date()
            today = datetime(2025, today_real.month, today_real.day).date()
            logger.info(f"使用默认的当前日期: {today}")

        # 打印详细的日期信息，便于调试
        logger.info(f"当前日期设置为: {today}")
        logger.info(f"当前星期几: {today.weekday()} (0=周一, 6=周日)")

        # 获取当前小时
        current_hour = datetime.now().hour

        # 0. 首先尝试从文本中直接提取日期
        extracted_date = self._extract_date_info(text, today)
        if extracted_date:
            target_date = extracted_date
            logger.info(f"从文本中直接提取到日期: {target_date}")

            # 检查提取的日期是否合理
            # 如果是"下周三"这样的表达，确保日期在7-13天内
            days_diff = (target_date - today).days
            if '下周' in text and (days_diff < 5 or days_diff > 13):
                logger.warning(f"提取的日期不合理: {target_date}，与当前日期相差{days_diff}天，重新计算")

                # 重新计算下周对应的日期
                weekday_map = {
                    '一': 0, '二': 1, '三': 2, '四': 3, '五': 4, '六': 5, '日': 6, '天': 6,
                    '1': 0, '2': 1, '3': 2, '4': 3, '5': 4, '6': 5, '7': 6, '0': 6
                }

                # 尝试提取"下周X"
                next_week_match = re.search(r'下周([一二三四五六日天0-7])', text)
                if next_week_match:
                    weekday = weekday_map.get(next_week_match.group(1))
                    if weekday is not None:
                        # 计算到下周X的天数
                        current_weekday = today.weekday()
                        days_until = 7 + (weekday - current_weekday)
                        if days_until >= 14:  # 如果超过14天，说明计算有误，修正为7天内
                            days_until -= 7

                        target_date = today + timedelta(days=days_until)
                        logger.info(f"重新计算'下周{next_week_match.group(1)}'日期: {target_date}")
                else:
                    # 如果没有明确的"下周X"，但有"下周"，则默认为下周一
                    if '下周' in text:
                        current_weekday = today.weekday()
                        days_until = 7 + (0 - current_weekday)  # 0表示周一
                        if days_until >= 14:
                            days_until -= 7

                        target_date = today + timedelta(days=days_until)
                        logger.info(f"检测到'下周'但无具体星期，默认为下周一: {target_date}")
        else:
            # 2. 如果无法直接提取日期，根据任务类型推断
            task_type = self._identify_student_task_type(text)
            logger.info(f"识别任务类型: {task_type}")

            # 根据任务类型分配默认日期
            if task_type == 'exam':
                # 考试类任务
                if '期中' in text:
                    # 期中考试通常在学期中间，假设在4周后
                    target_date = today + timedelta(days=28)
                    logger.info(f"期中考试，设置日期为4周后: {target_date}")
                elif '期末' in text:
                    # 期末考试通常在学期末，假设在8周后
                    target_date = today + timedelta(days=56)
                    logger.info(f"期末考试，设置日期为8周后: {target_date}")
                else:
                    # 普通考试默认3天后
                    target_date = today + timedelta(days=3)
                    # 如果3天后是周末，调整到下周一
                    if target_date.weekday() >= 5:
                        target_date = target_date + timedelta(days=(7-target_date.weekday()))
                    logger.info(f"普通考试，设置日期为3天后(避开周末): {target_date}")

            elif task_type == 'assignment':
                # 作业类任务
                if '截止' in text or 'deadline' in text:
                    # 有截止日期的作业，默认2天后
                    target_date = today + timedelta(days=2)
                    logger.info(f"有截止日期的作业，设置日期为2天后: {target_date}")
                else:
                    # 普通作业，默认1周后
                    target_date = today + timedelta(days=7)
                    logger.info(f"普通作业，设置日期为1周后: {target_date}")

            elif task_type == 'course':
                # 课程类任务
                # 检查是否包含特定课程时间
                if '早八' in text or '第一节' in text:
                    # 早八课程，安排在最近的工作日早上
                    if today.weekday() >= 5:  # 周末
                        target_date = today + timedelta(days=(7-today.weekday()))  # 下周一
                        logger.info(f"早八课程(当前是周末)，设置日期为下周一: {target_date}")
                    else:
                        target_date = today
                        logger.info(f"早八课程(当前是工作日)，设置日期为今天: {target_date}")
                elif '晚课' in text or '晚上课' in text:
                    # 晚上的课，安排在最近的工作日晚上
                    if today.weekday() >= 5:  # 周末
                        target_date = today + timedelta(days=(7-today.weekday()))  # 下周一
                        logger.info(f"晚课(当前是周末)，设置日期为下周一: {target_date}")
                    else:
                        target_date = today
                        logger.info(f"晚课(当前是工作日)，设置日期为今天: {target_date}")
                else:
                    # 普通课程，如果今天是周末，安排在下周一
                    if today.weekday() >= 5:  # 周末
                        target_date = today + timedelta(days=(7-today.weekday()))
                        logger.info(f"普通课程(当前是周末)，设置日期为下周一: {target_date}")
                    else:
                        target_date = today
                        logger.info(f"普通课程(当前是工作日)，设置日期为今天: {target_date}")

            elif task_type == 'meeting':
                # 会议类任务，通常在工作日
                if today.weekday() >= 5:  # 周末
                    target_date = today + timedelta(days=(7-today.weekday()))  # 下周一
                    logger.info(f"会议(当前是周末)，设置日期为下周一: {target_date}")
                elif datetime.now().hour >= 17:  # 工作日但已过下午5点
                    target_date = today + timedelta(days=1)  # 明天
                    if target_date.weekday() >= 5:  # 如果明天是周末
                        target_date = today + timedelta(days=(7-today.weekday()+1))  # 下周一
                    logger.info(f"会议(当前是工作日但已过下午5点)，设置日期为明天(避开周末): {target_date}")
                else:
                    target_date = today
                    logger.info(f"会议(当前是工作日且未过下午5点)，设置日期为今天: {target_date}")

            elif task_type == 'activity':
                # 活动类任务，通常在周末或下午
                if '社团' in text:
                    # 社团活动通常在周三或周五下午
                    if today.weekday() < 2:  # 周一或周二
                        target_date = today + timedelta(days=(2-today.weekday()))  # 周三
                        logger.info(f"社团活动(当前是周一或周二)，设置日期为本周三: {target_date}")
                    elif today.weekday() < 4:  # 周三或周四
                        target_date = today + timedelta(days=(4-today.weekday()))  # 周五
                        logger.info(f"社团活动(当前是周三或周四)，设置日期为本周五: {target_date}")
                    else:  # 周五、周六或周日
                        target_date = today + timedelta(days=(9-today.weekday()))  # 下周三
                        logger.info(f"社团活动(当前是周五、周六或周日)，设置日期为下周三: {target_date}")
                else:
                    # 其他活动优先安排在周末
                    if today.weekday() < 5:  # 工作日
                        target_date = today + timedelta(days=(5-today.weekday()))  # 周六
                        logger.info(f"活动(当前是工作日)，设置日期为本周六: {target_date}")
                    else:  # 已是周末
                        target_date = today
                        logger.info(f"活动(当前是周末)，设置日期为今天: {target_date}")

            elif task_type == 'study':
                # 学习类任务，优先安排在当天或明天
                if datetime.now().hour >= 20:  # 晚上8点后
                    target_date = today + timedelta(days=1)  # 明天
                    logger.info(f"学习任务(当前已过晚上8点)，设置日期为明天: {target_date}")
                else:
                    target_date = today
                    logger.info(f"学习任务(当前未过晚上8点)，设置日期为今天: {target_date}")

            elif task_type == 'project':
                # 项目类任务，通常需要一定准备时间
                target_date = today + timedelta(days=3)  # 3天后
                logger.info(f"项目任务，设置日期为3天后: {target_date}")

            elif task_type == 'social':
                # 社交类任务，优先安排在周末
                if today.weekday() < 5 and today.weekday() > 2:  # 周四或周五
                    target_date = today + timedelta(days=(5-today.weekday()))  # 周六
                    logger.info(f"社交任务(当前是周四或周五)，设置日期为本周六: {target_date}")
                elif today.weekday() < 3:  # 周一、周二或周三
                    target_date = today + timedelta(days=2)  # 2天后
                    logger.info(f"社交任务(当前是周一、周二或周三)，设置日期为2天后: {target_date}")
                else:  # 已是周末
                    target_date = today
                    logger.info(f"社交任务(当前是周末)，设置日期为今天: {target_date}")

            elif task_type == 'routine':
                # 日常任务，安排在今天
                target_date = today
                logger.info(f"日常任务，设置日期为今天: {target_date}")

            else:  # 默认类型
                # 默认安排在今天，如果当前时间已过下午3点，则安排在明天
                if datetime.now().hour >= 15:
                    target_date = today + timedelta(days=1)
                    logger.info(f"默认任务(当前已过下午3点)，设置日期为明天: {target_date}")
                else:
                    target_date = today
                    logger.info(f"默认任务(当前未过下午3点)，设置日期为今天: {target_date}")

        # 4. 更新日程信息
        schedule_info['date'] = target_date.strftime('%Y-%m-%d')
        schedule_info['is_date_ai_suggested'] = True
        logger.info(f"最终设置的日期: {schedule_info['date']}")

        # 5. 根据任务类型设置其他相关字段
        # 设置时间段
        if 'period' not in schedule_info or not schedule_info['period']:
            if '早上' in text or '早晨' in text or '上午' in text or '早八' in text:
                schedule_info['period'] = 'morning'
            elif '中午' in text:
                schedule_info['period'] = 'noon'
            elif '下午' in text:
                schedule_info['period'] = 'afternoon'
            elif '晚上' in text or '晚间' in text or '夜晚' in text:
                schedule_info['period'] = 'evening'
            else:
                # 根据任务类型设置默认时间段
                period_map = {
                    'exam': 'morning',
                    'course': 'morning',
                    'meeting': 'afternoon',
                    'activity': 'afternoon',
                    'study': 'evening',
                    'social': 'evening'
                }
                task_type = self._identify_student_task_type(text)
                schedule_info['period'] = period_map.get(task_type, 'morning')

        # 设置重要性
        if 'importance' not in schedule_info or not schedule_info['importance']:
            if any(keyword in text for keyword in ['重要', '紧急', '必须', '关键', '不能错过']):
                schedule_info['importance'] = 'high'
            else:
                importance_map = {
                    'exam': 'high',
                    'assignment': 'medium',
                    'course': 'medium',
                    'meeting': 'medium',
                    'project': 'medium',
                    'activity': 'low',
                    'social': 'low',
                    'routine': 'low'
                }
                task_type = self._identify_student_task_type(text)
                schedule_info['importance'] = importance_map.get(task_type, 'medium')

        return schedule_info

    def _complete_missing_time(self, schedule_info, text):
        """补全缺失的时间信息

        Args:
            schedule_info: 日程信息字典
            text: 原始文本

        Returns:
            Dict: 更新后的日程信息
        """
        # 如果已有完整时间，直接返回
        if ('start_time' in schedule_info and schedule_info['start_time'] and
            'end_time' in schedule_info and schedule_info['end_time']):
            return schedule_info

        # 获取任务类型
        task_type = self._identify_student_task_type(text)

        # 大学生特有表达方式处理
        start_time = None
        end_time = None

        # 1. 处理大学生特有的课程时间表达
        if '早八' in text:
            start_time = '08:00'
            end_time = '09:40'
        elif '第一节' in text or '第1节' in text:
            start_time = '08:00'
            end_time = '09:40'
        elif '第二节' in text or '第2节' in text:
            start_time = '10:00'
            end_time = '11:40'
        elif '第三节' in text or '第3节' in text:
            start_time = '14:00'
            end_time = '15:40'
        elif '第四节' in text or '第4节' in text:
            start_time = '16:00'
            end_time = '17:40'
        elif '晚自习' in text:
            start_time = '19:00'
            end_time = '21:00'
        elif '晚课' in text:
            start_time = '18:30'
            end_time = '20:10'

        # 2. 处理特定时间段表达
        elif '上午' in text and not start_time:
            if task_type == 'exam':
                start_time = '09:00'
                end_time = '11:00'
            else:
                start_time = '10:00'
                end_time = '11:30'
        elif '下午' in text and not start_time:
            if task_type == 'exam':
                start_time = '14:00'
                end_time = '16:00'
            elif task_type == 'meeting':
                start_time = '14:30'
                end_time = '16:00'
            else:
                start_time = '15:00'
                end_time = '16:30'
        elif '晚上' in text and not start_time:
            if task_type == 'study':
                start_time = '19:00'
                end_time = '21:00'
            elif task_type == 'social':
                start_time = '18:00'
                end_time = '20:00'
            else:
                start_time = '19:30'
                end_time = '21:00'
        elif '中午' in text and not start_time:
            start_time = '12:00'
            end_time = '13:00'

        # 3. 根据任务类型设置默认时间
        if not start_time:
            if task_type == 'exam':
                start_time = '09:00'
                end_time = '11:00'
            elif task_type == 'course':
                # 根据时间段决定课程时间
                if 'period' in schedule_info and schedule_info['period'] == 'afternoon':
                    start_time = '14:00'
                    end_time = '15:40'
                else:
                    start_time = '08:00'
                    end_time = '09:40'
            elif task_type == 'meeting':
                start_time = '15:00'
                end_time = '16:00'
            elif task_type == 'study':
                start_time = '19:00'
                end_time = '21:00'
            elif task_type == 'activity':
                if 'period' in schedule_info and schedule_info['period'] == 'afternoon':
                    start_time = '15:00'
                    end_time = '17:00'
                else:
                    start_time = '10:00'
                    end_time = '12:00'
            elif task_type == 'social':
                start_time = '18:00'
                end_time = '20:00'
            elif task_type == 'assignment':
                # 作业类通常没有固定时间，设置为全天
                start_time = '09:00'
                end_time = '18:00'
            elif task_type == 'project':
                start_time = '14:00'
                end_time = '17:00'
            else:
                # 默认时间
                if 'period' in schedule_info:
                    if schedule_info['period'] == 'morning':
                        start_time = '09:00'
                        end_time = '11:00'
                    elif schedule_info['period'] == 'noon':
                        start_time = '12:00'
                        end_time = '13:00'
                    elif schedule_info['period'] == 'afternoon':
                        start_time = '14:00'
                        end_time = '16:00'
                    else:  # evening
                        start_time = '19:00'
                        end_time = '21:00'
                else:
                    # 完全没有时间信息，默认为上午
                    start_time = '09:00'
                    end_time = '11:00'
                    schedule_info['period'] = 'morning'

        # 4. 更新时间信息
        if not schedule_info.get('start_time'):
            schedule_info['start_time'] = start_time
            schedule_info['is_time_ai_suggested'] = True

        if not schedule_info.get('end_time'):
            schedule_info['end_time'] = end_time
            schedule_info['is_time_ai_suggested'] = True

        # 5. 确保时间格式正确
        try:
            # 验证时间格式
            if schedule_info.get('start_time') == '待定' or not re.match(r'^\d{2}:\d{2}$', schedule_info.get('start_time', '')):
                logger.warning(f"开始时间格式不正确: {schedule_info.get('start_time')}，使用默认值")
                schedule_info['start_time'] = '09:00'
                schedule_info['is_time_ai_suggested'] = True

            if schedule_info.get('end_time') == '待定' or not re.match(r'^\d{2}:\d{2}$', schedule_info.get('end_time', '')):
                logger.warning(f"结束时间格式不正确: {schedule_info.get('end_time')}，使用默认值")
                schedule_info['end_time'] = '10:00'
                schedule_info['is_time_ai_suggested'] = True
        except Exception as e:
            # 如果验证过程中出现任何错误，使用默认值
            logger.error(f"验证时间格式失败: {str(e)}")
            schedule_info['start_time'] = '09:00'
            schedule_info['end_time'] = '10:00'
            schedule_info['is_time_ai_suggested'] = True

        return schedule_info

    def _extract_schedule_info(self, text: str, current_date: date) -> List[Dict[str, Any]]:
        """从文本中提取日程信息

        Args:
            text: 包含日程信息的文本
            current_date: 当前日期，用于日期计算

        Returns:
            List[Dict[str, Any]]: 提取的日程信息列表
        """
        schedules = []
        try:
            # 首先尝试提取全局日期信息
            global_date = self._extract_date_info(text, current_date)
            default_date = current_date

            # 预处理：移除可能的任务前缀
            text = re.sub(r'^.*?(我需要|计划|安排|日程|任务)[：:]\s*', '', text)
            text = re.sub(r'^.*?完成以下任务[：:]\s*', '', text)

            # 分割文本为单独的事件
            event_segments = self._split_text_into_events(text)

            # 处理每个事件段落
            for segment in event_segments:
                # 跳过空段落
                if not segment.strip():
                    continue

                # 尝试提取日期信息
                segment_date = self._extract_date_info(segment, current_date) or global_date or default_date

                # 无论如何都标记日期为AI建议，需要用户确认
                # 这确保用户总是会确认日期是否正确
                is_date_ai_suggested = True

                # 记录日志
                logger.info(f"段落: {segment}")
                logger.info(f"提取的日期: {segment_date}")
                logger.info(f"日期标记为需要确认: {is_date_ai_suggested}")

                # 尝试提取时间信息
                start_time, end_time, is_time_ai_suggested = self._parse_time(segment)

                # 提取标题和描述
                title = self._extract_title(segment)
                description = self._extract_description(segment)

                # 提取特定于此段落的关键点，而不是全局文本
                key_points = self._extract_reminders(segment)

                # 提取位置信息
                location, is_location_ai_suggested = self._extract_location(segment)

                # 如果位置为空，不标记为AI补全
                if not location or location.strip() == "":
                    location = ""
                    is_location_ai_suggested = False

                # 提取重要程度
                importance = self._determine_importance(segment)

                # 推断时间段
                period = self._determine_period(start_time)

                # 构建日程信息
                schedule = {
                    'title': title,
                    'description': description,
                    'date': segment_date.strftime('%Y-%m-%d'),
                    'start_time': start_time,
                    'end_time': end_time,
                    'period': period,
                    'importance': importance,
                    'status': 'pending',
                    'reminder': True,
                    'reminder_time': None,
                    'location': location,
                    'key_points': key_points,
                    'is_date_ai_suggested': is_date_ai_suggested,
                    'is_time_ai_suggested': is_time_ai_suggested,
                    'is_location_ai_suggested': is_location_ai_suggested
                }

                # 完善描述格式
                self._format_description(schedule)

                # 添加到日程列表
                schedules.append(schedule)

            return schedules

        except Exception as e:
            logger.exception(f"提取日程信息时出错: {str(e)}")
            return []

    def _split_text_into_events(self, text: str) -> List[str]:
        """将文本分割为单独的事件

        Args:
            text: 原始文本

        Returns:
            List[str]: 分割后的事件文本列表
        """
        # 首先按分号和换行符分割
        segments = re.split(r'[；;]|\n+', text)
        segments = [s.strip() for s in segments if s.strip()]

        # 如果没有足够的段落，尝试其他分割策略
        if len(segments) <= 1:
            # 尝试按句号分割
            segments = re.split(r'[。.]', text)
            segments = [s.strip() for s in segments if s.strip()]

            # 如果仍然不够，尝试按逗号分割但只保留可能是独立事件的片段
            if len(segments) <= 1:
                comma_segments = re.split(r'[，,]', text)

                # 检查每个片段是否包含时间信息，如果包含则视为独立事件
                time_segments = []
                for segment in comma_segments:
                    if re.search(r'(\d{1,2})[点:：时]|([早中下午晚]上|半夜|傍晚|深夜|凌晨)', segment):
                        time_segments.append(segment.strip())

                if len(time_segments) > 0:
                    segments = time_segments

        # 如果仍然没有得到多个段落，则把整个文本作为一个事件
        if len(segments) == 0:
            segments = [text]

        logger.info(f"分割得到 {len(segments)} 个事件片段")
        return segments

    def _extract_title(self, text: str) -> str:
        """提取日程标题"""
        try:
            # 1. 预处理文本
            cleaned_text = text.strip()

            # 2. 尝试从文本开头提取标题
            title_match = re.search(r'^([^，。；\n]+)', cleaned_text)
            if title_match:
                title = title_match.group(1).strip()
                # 如果标题过长，进行智能截断
                if len(title) > 50:
                    title = self._smart_truncate_title(title)
                return title

            # 3. 尝试提取课程信息
            course_match = re.search(r'科目[：:]\s*([^，。；\s]+)', cleaned_text)
            if course_match:
                return course_match.group(1).strip()

            # 4. 尝试提取时间后的第一个短语
            time_match = re.search(r'\d{1,2}[:：]\d{2}\s*([^，。；\n]+)', cleaned_text)
            if time_match:
                title = time_match.group(1).strip()
                if len(title) > 50:
                    title = self._smart_truncate_title(title)
                return title

            # 5. 提取第一行非空文本
            lines = [line.strip() for line in cleaned_text.split('\n') if line.strip()]
            if lines:
                title = lines[0]
                if len(title) > 50:
                    title = self._smart_truncate_title(title)
                return title

            return "未命名日程"

        except Exception as e:
            logger.warning(f"提取标题时出错: {str(e)}")
            return "未命名日程"

    def _smart_truncate_title(self, title: str) -> str:
        """智能截断标题"""
        try:
            # 1. 按标点符号分割
            parts = re.split(r'[，。；]', title)
            if parts:
                return parts[0].strip()

            # 2. 按空格分割
            parts = title.split()
            if parts:
                return parts[0].strip()

            # 3. 直接截断
            return title[:50] + "..."

        except Exception as e:
            logger.warning(f"智能截断标题时出错: {str(e)}")
            return title[:50] + "..."

    def _determine_period(self, start_time: str) -> str:
        """根据开始时间确定时间段

        Args:
            start_time: 开始时间 (HH:MM格式)

        Returns:
            str: 时间段 (morning/noon/afternoon/evening)
        """
        try:
            hour = int(start_time.split(':')[0])

            if 5 <= hour < 12:
                return 'morning'
            elif hour == 12:
                return 'noon'
            elif 12 < hour < 18:
                return 'afternoon'
            else:
                return 'evening'
        except:
            # 默认返回早上
            return 'morning'

    def _format_description(self, schedule: Dict[str, Any]) -> None:
        """格式化日程描述，使其更加清晰和有用

        Args:
            schedule: 日程信息字典

        Returns:
            None: 直接修改schedule字典
        """
        try:
            # 获取现有描述和关键点
            description = schedule.get('description', '')
            title = schedule.get('title', '')
            location = schedule.get('location', '')

            # 检查描述是否是关键点的列表（包含分隔符）
            if '、' in description or ',' in description or '，' in description:
                # 如果描述看起来像是关键点的集合，使用更好的描述
                if title:
                    if '课程' in title or '课' in title or '数据结构' in title:
                        description = "参加数据结构课程"
                    elif '项目' in title or '开发' in title:
                        description = "进行项目开发"
                    elif '健身' in title or '锻炼' in title or '跑步' in title:
                        description = "健身锻炼"
                    else:
                        if location:
                            description = f"在{location}进行{title}"
                        else:
                            description = f"进行{title}"
                else:
                    if '课程' in description or '课' in description or '教材' in description:
                        description = "参加课程学习"
                    elif '项目' in description or '开发' in description or '调试' in description:
                        description = "进行项目开发"
                    elif '健身' in description or '锻炼' in description or '跑步' in description:
                        description = "进行体育锻炼"
                    else:
                        description = "参加活动"

            # 如果描述为空，则使用活动的基本描述
            if not description:
                if title:
                    if location:
                        description = f"在{location}进行{title}"
                    else:
                        description = f"进行{title}"
                else:
                    description = "请查看事件详情"

            # 确保描述不超过30个字符
            if len(description) > 30:
                description = description[:27] + '...'

            # 更新描述
            schedule['description'] = description

            # 确保关键点仍然存在于schedule中
            if 'key_points' not in schedule:
                schedule['key_points'] = []

        except Exception as e:
            logger.warning(f"格式化描述时出错: {str(e)}")
            # 出错时设置一个默认描述
            if 'description' not in schedule or not schedule['description']:
                schedule['description'] = "事件详情"

    def _extract_date_info(self, text: str, current_date: date) -> date:
        """从文本中提取日期信息

        Args:
            text: 包含日期信息的文本
            current_date: 当前日期，用于日期计算

        Returns:
            date: 提取的日期，如果无法提取则返回None
        """
        try:
            logger.info(f"日期提取：当前参考日期为 {current_date}，星期{current_date.weekday()}（0=周一,6=周日）")

            # 首先检查是否有明确的日期格式，如"3月12日"
            explicit_month_day_match = re.search(r'(\d{1,2})月(\d{1,2})日', text)
            if explicit_month_day_match:
                month = int(explicit_month_day_match.group(1))
                day = int(explicit_month_day_match.group(2))

                logger.info(f"从文本中提取到明确的月日: {month}月{day}日")

                # 使用当前年份
                year = current_date.year

                # 检查日期是否有效
                if 1 <= month <= 12 and 1 <= day <= 31:
                    try:
                        extracted_date = date(year, month, day)

                        # 如果提取的日期已经过去，且月份在当前月份之前，则使用明年的日期
                        if extracted_date < current_date and month < current_date.month:
                            extracted_date = date(year + 1, month, day)
                            logger.info(f"日期已过去，使用明年的日期: {extracted_date}")

                        logger.info(f"最终确定的日期: {extracted_date}")
                        return self._validate_and_correct_date(extracted_date, text, current_date)
                    except ValueError as e:
                        logger.warning(f"无效的日期: {year}-{month}-{day}, {str(e)}")

            # 检查是否有直接的日期词
            for word, days in self.date_patterns['date_words'].items():
                if word in text:
                    result_date = current_date + timedelta(days=days)
                    logger.info(f"找到日期词 '{word}': {result_date}")
                    return result_date

            # 提取"X天后"格式
            days_later_match = re.search(self.date_patterns['days_later'], text)
            if days_later_match:
                days = int(days_later_match.group(1))
                if 1 <= days <= 30:  # 限制在30天内，以避免错误解析
                    future_date = current_date + timedelta(days=days)
                    logger.info(f"{days}天后: {future_date}")
                    return future_date

            # 尝试提取完整的日期格式 "YYYY-MM-DD" 或 "YYYY年MM月DD日"
            year_month_day_match = re.search(self.date_patterns['full_date'], text)
            if year_month_day_match:
                year = int(year_month_day_match.group(1))
                month = int(year_month_day_match.group(2))
                day = int(year_month_day_match.group(3))

                logger.info(f"从文本中提取到完整日期: {year}年{month}月{day}日")

                # 检查日期是否有效
                if 1 <= month <= 12 and 1 <= day <= 31:
                    try:
                        extracted_date = date(year, month, day)
                        return self._validate_and_correct_date(extracted_date, text, current_date)
                    except ValueError as e:
                        logger.warning(f"无效的日期: {year}-{month}-{day}, {str(e)}")

            # 检查"下下周X"格式
            after_next_week_match = re.search(self.date_patterns['after_next_week'], text)
            if after_next_week_match:
                target_weekday = self.weekday_map.get(after_next_week_match.group(2))
                if target_weekday is not None:
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        target_weekday,
                        is_after_next_week=True
                    )
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"下下下周X"格式 - 新增
            after_after_next_week_match = re.search(self.date_patterns['after_after_next_week'], text)
            if after_after_next_week_match:
                target_weekday = self.weekday_map.get(after_after_next_week_match.group(2))
                if target_weekday is not None:
                    # 计算下下下周日期（当前日期+21天）再调整到目标星期几
                    base_date = current_date + timedelta(days=21)
                    # 调整到目标星期几
                    days_diff = (target_weekday - base_date.weekday()) % 7
                    extracted_date = base_date + timedelta(days=days_diff)
                    logger.info(f"下下下周{after_after_next_week_match.group(2)}: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"下个月第X个周Y"格式 - 新增
            next_month_nth_weekday_match = re.search(self.date_patterns['next_month_nth_weekday'], text)
            if next_month_nth_weekday_match:
                nth = int(next_month_nth_weekday_match.group(2))
                target_weekday = self.weekday_map.get(next_month_nth_weekday_match.group(5))
                if target_weekday is not None and 1 <= nth <= 5:
                    # 计算下个月的第一天
                    year = current_date.year
                    month = current_date.month + 1
                    if month > 12:
                        month = 1
                        year += 1
                    first_day = date(year, month, 1)

                    # 计算下个月第一个目标星期几的日期
                    days_until_first = (target_weekday - first_day.weekday()) % 7
                    first_target_day = first_day + timedelta(days=days_until_first)

                    # 计算第n个目标星期几
                    extracted_date = first_target_day + timedelta(days=(nth-1)*7)

                    # 确保日期仍在下个月内
                    if extracted_date.month != month:
                        logger.warning(f"计算的日期 {extracted_date} 不在目标月份内，使用当月最后一个 {target_weekday}")
                        # 使用当月最后一个该星期几
                        last_day_of_month = first_day.replace(month=month+1 if month < 12 else 1,
                                                             year=year if month < 12 else year+1) - timedelta(days=1)
                        days_diff = (target_weekday - last_day_of_month.weekday()) % 7
                        if days_diff > 0:
                            extracted_date = last_day_of_month - timedelta(days=7-days_diff)
                        else:
                            extracted_date = last_day_of_month

                    logger.info(f"下个月第{nth}个周{target_weekday}: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"本月第X个周Y"格式 - 新增
            this_month_nth_weekday_match = re.search(self.date_patterns['this_month_nth_weekday'], text)
            if this_month_nth_weekday_match:
                nth = int(this_month_nth_weekday_match.group(3))
                target_weekday = self.weekday_map.get(this_month_nth_weekday_match.group(6))
                if target_weekday is not None and 1 <= nth <= 5:
                    # 计算本月的第一天
                    year = current_date.year
                    month = current_date.month
                    first_day = date(year, month, 1)

                    # 计算本月第一个目标星期几的日期
                    days_until_first = (target_weekday - first_day.weekday()) % 7
                    first_target_day = first_day + timedelta(days=days_until_first)

                    # 计算第n个目标星期几
                    extracted_date = first_target_day + timedelta(days=(nth-1)*7)

                    # 确保日期仍在本月内
                    if extracted_date.month != month:
                        logger.warning(f"计算的日期 {extracted_date} 不在目标月份内，使用当月最后一个 {target_weekday}")
                        # 使用当月最后一个该星期几
                        last_day_of_month = first_day.replace(month=month+1 if month < 12 else 1,
                                                             year=year if month < 12 else year+1) - timedelta(days=1)
                        days_diff = (target_weekday - last_day_of_month.weekday()) % 7
                        if days_diff > 0:
                            extracted_date = last_day_of_month - timedelta(days=7-days_diff)
                        else:
                            extracted_date = last_day_of_month

                    # 如果计算的日期已过，则使用下个月的对应日期
                    if extracted_date < current_date:
                        logger.info(f"本月第{nth}个周{target_weekday} ({extracted_date}) 已过，尝试使用下个月")
                        # 递归调用，将文本修改为下个月的表达
                        modified_text = text.replace(this_month_nth_weekday_match.group(1), "下")
                        return self._extract_date_info(modified_text, current_date)

                    logger.info(f"本月第{nth}个周{target_weekday}: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"下周X"格式
            next_week_match = re.search(self.date_patterns['next_week'], text)
            if next_week_match:
                target_weekday = self.weekday_map.get(next_week_match.group(2))
                if target_weekday is not None:
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        target_weekday,
                        is_next_week=True
                    )
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"本周X"/"这周X"格式
            this_week_match = re.search(self.date_patterns['this_week'], text)
            if this_week_match:
                target_weekday = self.weekday_map.get(this_week_match.group(3))
                if target_weekday is not None:
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        target_weekday,
                        is_this_week=True
                    )
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"星期X"或"礼拜X"格式
            weekday_full_match = re.search(self.date_patterns['weekday_full'], text)
            if weekday_full_match:
                target_weekday = self.weekday_map.get(weekday_full_match.group(2))
                if target_weekday is not None:
                    # 根据上下文判断是本周还是下周
                    if '下' in text[:weekday_full_match.start()]:
                        extracted_date = self._calculate_weekday_date(
                            current_date,
                            target_weekday,
                            is_next_week=True
                        )
                    elif '本' in text[:weekday_full_match.start()] or '这' in text[:weekday_full_match.start()]:
                        extracted_date = self._calculate_weekday_date(
                            current_date,
                            target_weekday,
                            is_this_week=True
                        )
                    else:
                        # 默认为最近的未来日期 - 修复关键部分
                        is_this_week = False
                        
                        # 如果目标日期大于当前日期，可能是本周
                        if target_weekday > current_date.weekday():
                            is_this_week = True
                            logger.info(f"星期{weekday_full_match.group(2)}大于当前日期，视为本周")
                        else:
                            # 否则为下周
                            logger.info(f"星期{weekday_full_match.group(2)}小于或等于当前日期，视为下周")
                        
                        extracted_date = self._calculate_weekday_date(
                            current_date,
                            target_weekday,
                            is_this_week=is_this_week
                        )
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查不带前缀的"周X"格式
            weekday_only_match = re.search(self.date_patterns['weekday_only'], text)
            if weekday_only_match:
                # 获取匹配的周几（注意我们改变了捕获组的位置）
                target_day = weekday_only_match.group(4)  # 现在是第4个捕获组
                target_weekday = self.weekday_map.get(target_day)
                
                if target_weekday is not None:
                    # 关键修复：根据当前日期和目标日期的关系来智能判断
                    is_this_week = False
                    
                    # 如果目标日期大于当前日期，即在未来，视为本周
                    if target_weekday > current_date.weekday():
                        is_this_week = True
                        logger.info(f"周{target_day}大于当前日期，视为本周")
                    else:
                        # 否则视为下周
                        logger.info(f"周{target_day}小于或等于当前日期，视为下周")
                    
                    # 使用修正后的逻辑计算日期
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        target_weekday,
                        is_this_week=is_this_week
                    )
                    
                    logger.info(f"提取到周{target_day}，计算得到日期: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"周末"表达
            weekend_match = re.search(self.date_patterns['weekend'], text)
            if weekend_match:
                # 判断是本周末还是下周末
                is_next_week = '下' in weekend_match.group(0)

                # 获取当前星期几
                current_weekday = current_date.weekday()

                # 计算到周六的天数
                days_to_saturday = 5 - current_weekday  # 5是周六的索引（Python中）

                # 如果已经是周六或周日，或者指定了下周末
                if current_weekday >= 5 or is_next_week:
                    days_to_saturday += 7

                # 返回周六的日期
                weekend_date = current_date + timedelta(days=days_to_saturday)
                logger.info(f"周末: {weekend_date}, 当前日期: {current_date}, 计算天数: {days_to_saturday}")
                return weekend_date

            # 提取"下个月X日"、"下月X日"格式
            next_month_match = re.search(self.date_patterns['next_month'], text)
            if next_month_match:
                day = int(next_month_match.group(2))

                # 计算下个月的日期
                year = current_date.year
                month = current_date.month + 1
                if month > 12:
                    month = 1
                    year += 1

                # 检查日期是否有效
                if 1 <= day <= 31:
                    try:
                        extracted_date = date(year, month, day)
                        return self._validate_and_correct_date(extracted_date, text, current_date)
                    except ValueError as e:
                        logger.warning(f"无效的下月日期: {year}-{month}-{day}, {str(e)}")
                        # 尝试使用月底日期
                        try:
                            if month == 2:
                                # 二月特殊处理
                                if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
                                    # 闰年
                                    return date(year, month, min(day, 29))
                                else:
                                    # 平年
                                    return date(year, month, min(day, 28))
                            else:
                                # 其他月份
                                month_days = {1:31, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31}
                                return date(year, month, min(day, month_days[month]))
                        except Exception as e:
                            logger.warning(f"处理月底日期失败: {str(e)}")

            # 提取"这个月X日"、"本月X日"格式
            this_month_match = re.search(self.date_patterns['this_month'], text)
            if this_month_match:
                day = int(this_month_match.group(3))

                # 使用当前年月
                year = current_date.year
                month = current_date.month

                # 检查日期是否有效
                if 1 <= day <= 31:
                    try:
                        result_date = date(year, month, day)

                        # 如果日期已过，使用下个月的同一天
                        if result_date < current_date:
                            month += 1
                            if month > 12:
                                month = 1
                                year += 1
                            try:
                                result_date = date(year, month, day)
                                logger.info(f"日期已过去，使用下个月的日期: {result_date}")
                            except ValueError:
                                # 处理无效日期（如2月30日）
                                logger.warning(f"下个月的日期无效: {year}-{month}-{day}")
                                # 尝试获取月底日期
                                if month == 2:
                                    if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
                                        day = 29  # 闰年二月
                                    else:
                                        day = 28  # 平年二月
                                elif month in [4, 6, 9, 11]:
                                    day = 30  # 30天的月份
                                else:
                                    day = 31  # 31天的月份

                                result_date = date(year, month, day)
                                logger.info(f"调整为月底日期: {result_date}")

                        return self._validate_and_correct_date(result_date, text, current_date)
                    except ValueError as e:
                        logger.warning(f"无效的日期: {year}-{month}-{day}, {str(e)}")

            # 检查斜杠或横杠分隔的日期格式 (如 3/15, 3-15)
            slash_date_match = re.search(self.date_patterns['slash_date'], text)
            if slash_date_match:
                month = int(slash_date_match.group(1))
                day = int(slash_date_match.group(2))

                logger.info(f"从文本中提取到斜杠/横杠分隔的日期: {month}/{day}")

                # 使用当前年份
                year = current_date.year

                # 检查日期是否有效
                if 1 <= month <= 12 and 1 <= day <= 31:
                    try:
                        extracted_date = date(year, month, day)

                        # 如果提取的日期已经过去，且月份在当前月份之前，则使用明年的日期
                        if extracted_date < current_date and month < current_date.month:
                            extracted_date = date(year + 1, month, day)
                            logger.info(f"日期已过去，使用明年的日期: {extracted_date}")

                        return extracted_date
                    except ValueError as e:
                        logger.warning(f"无效的日期: {year}-{month}-{day}, {str(e)}")

            # 提取"X号"格式
            day_with_hao_match = re.search(self.date_patterns['day_with_hao'], text)
            if day_with_hao_match:
                day = int(day_with_hao_match.group(1))
                logger.info(f"从文本中提取到X号格式: {day}号")

                # 使用当前年月
                year = current_date.year
                month = current_date.month

                # 检查日期是否有效
                if 1 <= day <= 31:
                    try:
                        result_date = date(year, month, day)

                        # 如果日期已过，使用下个月的同一天
                        if result_date < current_date:
                            month += 1
                            if month > 12:
                                month = 1
                                year += 1
                            try:
                                result_date = date(year, month, day)
                                logger.info(f"日期已过去，使用下个月的日期: {result_date}")
                            except ValueError:
                                # 处理无效日期
                                logger.warning(f"下个月的日期无效: {year}-{month}-{day}")
                                # 尝试获取月底日期
                                if month == 2:
                                    if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
                                        day = 29  # 闰年二月
                                    else:
                                        day = 28  # 平年二月
                                elif month in [4, 6, 9, 11]:
                                    day = 30  # 30天的月份
                                else:
                                    day = 31  # 31天的月份

                                result_date = date(year, month, day)
                                logger.info(f"调整为月底日期: {result_date}")

                        return result_date
                    except ValueError as e:
                        logger.warning(f"无效的日期: {year}-{month}-{day}, {str(e)}")

            # 尝试提取单独的"DD日"格式，使用当前月份
            day_match = re.search(self.date_patterns['day_only'], text)
            if day_match and not explicit_month_day_match and not year_month_day_match and not this_month_match and not next_month_match and not day_with_hao_match:
                day = int(day_match.group(1))
                logger.info(f"从文本中提取到单独的日期: {day}日")

                # 使用当前年月
                year = current_date.year
                month = current_date.month

                # 检查日期是否有效
                if 1 <= day <= 31:
                    try:
                        result_date = date(year, month, day)

                        # 如果日期已过，使用下个月的同一天
                        if result_date < current_date:
                            month += 1
                            if month > 12:
                                month = 1
                                year += 1
                            try:
                                result_date = date(year, month, day)
                                logger.info(f"日期已过去，使用下个月的日期: {result_date}")
                            except ValueError:
                                # 处理无效日期（如2月30日）
                                logger.warning(f"下个月的日期无效: {year}-{month}-{day}")
                                # 尝试获取月底日期
                                if month == 2:
                                    if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
                                        day = 29  # 闰年二月
                                    else:
                                        day = 28  # 平年二月
                                elif month in [4, 6, 9, 11]:
                                    day = 30  # 30天的月份
                                else:
                                    day = 31  # 31天的月份

                                result_date = date(year, month, day)
                                logger.info(f"调整为月底日期: {result_date}")

                        return result_date
                    except ValueError as e:
                        logger.warning(f"无效的日期: {year}-{month}-{day}, {str(e)}")

            # 检查"周几"格式
            weekday_match = re.search(self.date_patterns['weekday'], text)
            if weekday_match:
                target_weekday = self.weekday_map.get(weekday_match.group(1))
                if target_weekday is not None:
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        target_weekday
                    )
                    self.logger.info(f"从文本中提取到周几表达式: 周{weekday_match.group(1)}, 计算得到日期: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"周一到周日"这样的范围表达式
            weekday_range_match = re.search(r'周([一二三四五六日天])到周([一二三四五六日天])', text)
            if weekday_range_match:
                # 提取范围的起始和结束周几
                start_weekday = self.weekday_map.get(weekday_range_match.group(1))
                end_weekday = self.weekday_map.get(weekday_range_match.group(2))

                if start_weekday is not None and end_weekday is not None:
                    # 对于范围表达式，我们返回范围的第一天
                    # 如果需要处理整个范围，可以在调用方进行处理
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        start_weekday
                    )
                    self.logger.info(f"检测到周几范围表达式: 周{weekday_range_match.group(1)}到周{weekday_range_match.group(2)}, 使用起始日期: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"周（一到日）"这样的范围表达式
            weekday_range_paren_match = re.search(self.date_patterns['weekday_range_paren'], text)
            if weekday_range_paren_match:
                # 提取范围的起始和结束周几
                start_weekday = self.weekday_map.get(weekday_range_paren_match.group(1))
                end_weekday = self.weekday_map.get(weekday_range_paren_match.group(2))

                if start_weekday is not None and end_weekday is not None:
                    # 对于范围表达式，我们返回范围的第一天
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        start_weekday
                    )
                    self.logger.info(f"检测到周几括号范围表达式: 周（{weekday_range_paren_match.group(1)}到{weekday_range_paren_match.group(2)}）, 使用起始日期: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查特殊的"周（一到日）"格式，不使用"到"连接
            special_weekday_range_match = re.search(r'周[（(]([一二三四五六日天]).*?([一二三四五六日天])[）)]', text)
            if special_weekday_range_match:
                # 提取范围的起始和结束周几
                start_weekday = self.weekday_map.get(special_weekday_range_match.group(1))
                end_weekday = self.weekday_map.get(special_weekday_range_match.group(2))

                if start_weekday is not None and end_weekday is not None:
                    # 对于范围表达式，我们返回范围的第一天
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        start_weekday
                    )
                    self.logger.info(f"检测到特殊周几括号范围表达式: 周（{special_weekday_range_match.group(1)}...{special_weekday_range_match.group(2)}）, 使用起始日期: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"周一到日"这样的特殊格式（没有第二个"周"字）
            special_range_match = re.search(r'周([一二三四五六日天])到([一二三四五六日天])', text)
            if special_range_match:
                # 提取范围的起始和结束周几
                start_weekday = self.weekday_map.get(special_range_match.group(1))
                end_weekday = self.weekday_map.get(special_range_match.group(2))

                if start_weekday is not None and end_weekday is not None:
                    # 对于范围表达式，我们返回范围的第一天
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        start_weekday
                    )
                    self.logger.info(f"检测到特殊周几范围表达式: 周{special_range_match.group(1)}到{special_range_match.group(2)}, 使用起始日期: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查不带前缀的周几表达式（如"周一"、"周二"等）
            weekday_only_match = re.search(self.date_patterns['weekday_only'], text)
            if weekday_only_match:
                # 获取匹配的周几，可能在group(1)或group(2)中
                weekday_str = weekday_only_match.group(1) or weekday_only_match.group(2)
                target_weekday = self.weekday_map.get(weekday_str)

                if target_weekday is not None:
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        target_weekday
                    )
                    self.logger.info(f"检测到不带前缀的周几表达式: 周{weekday_str}, 计算得到日期: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"周一到日下午3点"这样的特殊格式
            special_time_range_match = re.search(r'周([一二三四五六日天])到([一二三四五六日天])[^,，。；！？]*?([早中下晚]上?|上午|下午|晚上).*?(\d{1,2})[点:：]', text)
            if special_time_range_match:
                # 提取范围的起始和结束周几
                start_weekday = self.weekday_map.get(special_time_range_match.group(1))
                end_weekday = self.weekday_map.get(special_time_range_match.group(2))
                time_period = special_time_range_match.group(3)
                hour = int(special_time_range_match.group(4))

                if start_weekday is not None and end_weekday is not None:
                    # 检查是否指定了周次
                    is_this_week = '本周' in text or '这周' in text
                    is_next_week = '下周' in text or '下个周' in text
                    is_after_next_week = '下下周' in text

                    # 对于范围表达式，我们返回范围的第一天
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        start_weekday,
                        is_next_week=is_next_week,
                        is_this_week=is_this_week,
                        is_after_next_week=is_after_next_week
                    )
                    self.logger.info(f"检测到带时间的周几范围表达式: 周{special_time_range_match.group(1)}到{special_time_range_match.group(2)}{time_period}{hour}点, 使用起始日期: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            # 检查"周一到日"这样的特殊格式（没有第二个"周"字）
            special_range_match = re.search(r'周([一二三四五六日天])到([一二三四五六日天])', text)
            if special_range_match and not weekday_range_match and not special_time_range_match:
                # 提取范围的起始和结束周几
                start_weekday = self.weekday_map.get(special_range_match.group(1))
                end_weekday = self.weekday_map.get(special_range_match.group(2))

                if start_weekday is not None and end_weekday is not None:
                    # 检查是否指定了周次
                    is_this_week = '本周' in text or '这周' in text
                    is_next_week = '下周' in text or '下个周' in text
                    is_after_next_week = '下下周' in text

                    # 对于范围表达式，我们返回范围的第一天
                    extracted_date = self._calculate_weekday_date(
                        current_date,
                        start_weekday,
                        is_next_week=is_next_week,
                        is_this_week=is_this_week,
                        is_after_next_week=is_after_next_week
                    )
                    self.logger.info(f"检测到特殊周几范围表达式: 周{special_range_match.group(1)}到{special_range_match.group(2)}, 使用起始日期: {extracted_date}")
                    return self._validate_and_correct_date(extracted_date, text, current_date)

            return None

        except Exception as e:
            logger.exception(f"提取日期时出错: {str(e)}")
            return None

    def _has_explicit_date(self, text: str) -> bool:
        """检查文本中是否包含明确的日期

        Args:
            text: 要检查的文本

        Returns:
            bool: 如果文本中包含明确的日期，返回True；否则返回False
        """
        # 检查常见的日期模式
        date_patterns = [
            r'\d{4}[-/年]\d{1,2}[-/月]\d{1,2}[日号]?',  # 2023-01-01, 2023年01月01日
            r'\d{1,2}[-/月]\d{1,2}[日号]?',  # 01-01, 01月01日
            r'[今明后]天',  # 今天，明天，后天
            r'下?周[一二三四五六日天]',  # 周一，下周一
            r'[下上]个?月\d{1,2}[日号]?',  # 下月1日，上个月15号
            r'\d{1,2}[日号]?',  # 1日，15号
        ]

        for pattern in date_patterns:
            if re.search(pattern, text):
                return True

        # 检查特定的日期关键词
        date_keywords = ['今天', '明天', '后天', '大后天', '周末', '下周', '下个月', '本周', '本月']
        for keyword in date_keywords:
            if keyword in text:
                return True

        return False

    def _extract_description(self, text: str) -> str:
        """从文本中提取主要描述

        Args:
            text: 要提取描述的文本

        Returns:
            str: 提取的描述
        """
        try:
            # 首先尝试使用缓存
            cache_key = f"description_extract:{hashlib.md5(text.encode()).hexdigest()}"
            cached_result = self._get_cache(cache_key)
            if cached_result:
                logger.info("使用缓存的描述提取结果")
                return cached_result

            # 确定事件类型，用于更精确的描述提取
            event_type = self._determine_event_type(text)
            logger.info(f"确定的事件类型: {event_type}")

            # 预处理文本，移除干扰信息
            cleaned_text = self._preprocess_for_description(text)

            # 尝试使用AI提取描述（如果配置了AI API）
            if hasattr(settings, 'AI_API_KEY') and settings.AI_API_KEY and len(text) > 20:
                try:
                    ai_description = self._extract_description_with_ai(text, event_type)
                    if ai_description:
                        # 缓存结果
                        self._set_cache(cache_key, ai_description, 3600)  # 缓存1小时
                        return ai_description
                except Exception as e:
                    logger.warning(f"使用AI提取描述时出错: {str(e)}")

            # 如果AI提取失败，使用增强的规则匹配
            # 1. 基于事件类型的特定描述提取
            description = self._extract_description_by_event_type(cleaned_text, event_type)
            if description:
                # 缓存结果
                self._set_cache(cache_key, description, 3600)  # 缓存1小时
                return description

            # 2. 如果没有匹配特定类型，提取核心活动
            sentences = re.split(r'[，。；,.;]', cleaned_text)
            if sentences:
                first_sentence = sentences[0].strip()
                # 智能截取：尝试在语义边界处截断
                if 5 <= len(first_sentence) <= 25:
                    description = first_sentence
                elif len(first_sentence) > 25:
                    # 尝试在最后一个完整词处截断
                    cut_point = 25
                    while cut_point > 15 and not self._is_word_boundary(first_sentence, cut_point):
                        cut_point -= 1
                    description = first_sentence[:cut_point]

                    # 确保描述不以标点符号结尾
                    description = re.sub(r'[，。；,.;!?！？]$', '', description)

                    # 如果截断后太短，使用固定长度
                    if len(description) < 10:
                        description = first_sentence[:20]
                else:
                    # 如果第一句话太短，尝试合并前两句
                    if len(sentences) > 1:
                        combined = (first_sentence + sentences[1].strip()).strip()
                        if len(combined) <= 25:
                            description = combined
                        else:
                            description = combined[:20]
                    else:
                        description = first_sentence

                # 缓存结果
                self._set_cache(cache_key, description, 3600)  # 缓存1小时
                return description

            # 最后的备选描述 - 基于事件类型提供更具体的默认描述
            default_descriptions = {
                '学习活动': "参加学习活动",
                '考试活动': "参加考试",
                '演讲活动': "进行演讲",
                '会议活动': "参加会议",
                '社交活动': "社交活动",
                '面试活动': "参加面试",
                '休闲活动': "休闲活动",
                '运动活动': "体育锻炼"
            }

            description = default_descriptions.get(event_type, "查看活动详情")

            # 缓存结果
            self._set_cache(cache_key, description, 3600)  # 缓存1小时
            return description

        except Exception as e:
            logger.warning(f"提取描述时出错: {str(e)}")
            return "活动详情"

    def _preprocess_for_description(self, text: str) -> str:
        """预处理文本，移除干扰信息，用于描述提取

        Args:
            text: 原始文本

        Returns:
            str: 处理后的文本
        """
        # 移除时间信息
        cleaned_text = re.sub(r'(\d{1,2})[点:：时](\d{1,2})?分?', '', text)
        cleaned_text = re.sub(r'[早中下晚]上|上午|下午|凌晨|傍晚|晚上|早晨', '', cleaned_text)

        # 移除日期信息
        cleaned_text = re.sub(r'(今|明|后)[天日]', '', cleaned_text)
        cleaned_text = re.sub(r'周[一二三四五六日天]', '', cleaned_text)
        cleaned_text = re.sub(r'下周[一二三四五六日天]', '', cleaned_text)
        cleaned_text = re.sub(r'(\d{1,2})月(\d{1,2})[日号]', '', cleaned_text)
        cleaned_text = re.sub(r'(\d{4})年(\d{1,2})月(\d{1,2})[日号]', '', cleaned_text)

        # 移除提醒信息
        cleaned_text = re.sub(r'(记得|别忘了|需要|带上|携带).*?[，。；！？,\.;!?]', '', cleaned_text)

        # 移除常见的无意义前缀
        cleaned_text = re.sub(r'^(我要|我需要|我想|我打算|我计划)', '', cleaned_text)

        return cleaned_text.strip()

    def _is_word_boundary(self, text: str, position: int) -> bool:
        """检查给定位置是否是词语边界

        Args:
            text: 文本
            position: 位置索引

        Returns:
            bool: 如果是词语边界返回True
        """
        if position <= 0 or position >= len(text):
            return True

        # 检查是否在标点符号处
        if text[position] in '，。；,.;!?！？':
            return True

        # 检查前后字符是否构成自然断点
        # 例如：名词+动词、形容词+名词等
        return False  # 简化实现，实际中可以使用更复杂的NLP方法

    def _extract_description_with_ai(self, text: str, event_type: str) -> str:
        """使用AI提取描述

        Args:
            text: 原始文本
            event_type: 事件类型

        Returns:
            str: AI提取的描述
        """
        try:
            # 构建提示词
            prompt = f"""
请从以下文本中提取一个简短的日程描述（不超过25个字符）。
这是一个{event_type}，请提取最能概括活动内容的核心描述。
只需返回描述文本，不要包含任何解释或其他内容。

文本：{text}
"""
            # 调用AI API
            response = self._call_ai_api(prompt, temperature=0.3)

            # 处理响应
            if isinstance(response, str):
                # 清理响应
                description = response.strip()
                # 移除可能的引号和多余空格
                description = re.sub(r'^["\']+|["\']+$', '', description).strip()
                # 限制长度
                if len(description) > 25:
                    description = description[:25]
                return description
            else:
                # 如果是流式响应，收集完整内容
                full_response = ""
                for chunk in response:
                    full_response += chunk

                # 清理响应
                description = full_response.strip()
                # 移除可能的引号和多余空格
                description = re.sub(r'^["\']+|["\']+$', '', description).strip()
                # 限制长度
                if len(description) > 25:
                    description = description[:25]
                return description

        except Exception as e:
            logger.warning(f"使用AI提取描述时出错: {str(e)}")
            return ""

    def _extract_description_by_event_type(self, text: str, event_type: str) -> str:
        """基于事件类型提取描述

        Args:
            text: 清理后的文本
            event_type: 事件类型

        Returns:
            str: 提取的描述
        """
        # 使用更全面的关键词匹配
        event_patterns = {
            '学习活动': [
                # 课程相关
                (r'([^，。；\s]{1,8})[课程]', lambda m: f"参加{m.group(1)}课程"),
                (r'学习([^，。；\s]{1,8})', lambda m: f"学习{m.group(1)}"),
                (r'复习([^，。；\s]{1,8})', lambda m: f"复习{m.group(1)}"),
                (r'预习([^，。；\s]{1,8})', lambda m: f"预习{m.group(1)}"),
                (r'(自习|图书馆|阅读)', lambda m: "自习学习"),
                # 默认
                (r'', lambda m: "学习活动")
            ],
            '考试活动': [
                (r'([^，。；\s]{1,8})[考试]', lambda m: f"{m.group(1)}考试"),
                (r'考试([^，。；\s]{1,8})', lambda m: f"考试{m.group(1)}"),
                (r'(测验|测试|笔试|口试)', lambda m: f"{m.group(1)}"),
                # 默认
                (r'', lambda m: "参加考试")
            ],
            '演讲活动': [
                (r'([^，。；\s]{1,8})[演讲]', lambda m: f"{m.group(1)}演讲"),
                (r'演讲([^，。；\s]{1,8})', lambda m: f"演讲{m.group(1)}"),
                (r'(汇报|讲解|发言|宣讲)', lambda m: f"{m.group(1)}"),
                # 默认
                (r'', lambda m: "进行演讲")
            ],
            '会议活动': [
                (r'([^，。；\s]{1,8})[会议]', lambda m: f"{m.group(1)}会议"),
                (r'会议([^，。；\s]{1,8})', lambda m: f"会议{m.group(1)}"),
                (r'(讨论|碰头|沟通|交流|研讨)', lambda m: f"{m.group(1)}会议"),
                # 默认
                (r'', lambda m: "参加会议")
            ],
            '社交活动': [
                (r'([^，。；\s]{1,8})[聚餐]', lambda m: f"{m.group(1)}聚餐"),
                (r'(吃饭|用餐|聚会|聚集)', lambda m: f"{m.group(1)}"),
                # 默认
                (r'', lambda m: "社交活动")
            ],
            '面试活动': [
                (r'([^，。；\s]{1,8})[面试]', lambda m: f"{m.group(1)}面试"),
                (r'面试([^，。；\s]{1,8})', lambda m: f"面试{m.group(1)}"),
                (r'(招聘|应聘|求职)', lambda m: f"{m.group(1)}"),
                # 默认
                (r'', lambda m: "参加面试")
            ],
            '休闲活动': [
                (r'(休息|放松|娱乐|游玩)', lambda m: f"{m.group(1)}时间"),
                (r'(旅游|观光|游览|参观)([^，。；\s]{1,8})?',
                 lambda m: f"{m.group(1)}{m.group(2) if m.group(2) else ''}"),
                # 默认
                (r'', lambda m: "休闲活动")
            ],
            '运动活动': [
                (r'([^，。；\s]{1,8})[运动]', lambda m: f"{m.group(1)}运动"),
                (r'(锻炼|健身|跑步|游泳|打球)', lambda m: f"{m.group(1)}"),
                (r'健身房', lambda m: "健身房锻炼"),
                # 默认
                (r'', lambda m: "体育锻炼")
            ]
        }

        # 获取当前事件类型的模式
        patterns = event_patterns.get(event_type, [])

        # 尝试匹配每个模式
        for pattern, formatter in patterns:
            if not pattern:  # 默认模式
                continue

            match = re.search(pattern, text)
            if match:
                return formatter(match)

        # 如果没有匹配到具体模式，使用默认描述
        for pattern, formatter in patterns:
            if not pattern:  # 默认模式
                return formatter(None)

        # 最后的备选
        return "活动详情"

    def _default_extract_description(self, text: str) -> str:
        """默认的描述提取方法，用作备份

        Args:
            text: 要提取描述的文本

        Returns:
            str: 提取的描述
        """
        return self._extract_description(text)

    def _extract_reminders(self, text: str) -> List[str]:
        """从文本中提取关键提醒

        Args:
            text: 要提取提醒的文本

        Returns:
            List[str]: 提取的关键提醒列表
        """
        try:
            # 首先尝试使用缓存
            cache_key = f"reminders_extract:{hashlib.md5(text.encode()).hexdigest()}"
            cached_result = self._get_cache(cache_key)
            if cached_result:
                logger.info("使用缓存的提醒提取结果")
                return cached_result

            # 确定事件类型
            event_type = self._determine_event_type(text)
            logger.info(f"提取提醒的事件类型: {event_type}")

            # 尝试使用AI提取提醒（如果配置了AI API）
            if hasattr(settings, 'AI_API_KEY') and settings.AI_API_KEY and len(text) > 30:
                try:
                    ai_reminders = self._extract_reminders_with_ai(text, event_type)
                    if ai_reminders:
                        # 缓存结果
                        self._set_cache(cache_key, ai_reminders, 3600)  # 缓存1小时
                        return ai_reminders
                except Exception as e:
                    logger.warning(f"使用AI提取提醒时出错: {str(e)}")

            # 如果AI提取失败，使用增强的规则匹配
            # 使用字典去除重复项
            reminder_dict = {}

            # 1. 提取显式提到的物品（优先级最高）
            explicit_items = self._extract_explicit_reminders(text)
            for item in explicit_items:
                reminder_dict[item.lower()] = item

            # 2. 基于事件类型提取隐式提醒
            implicit_items = self._extract_implicit_reminders_by_event_type(text, event_type)
            for item in implicit_items:
                if len(reminder_dict) < 5:  # 限制最多5个提醒
                    reminder_dict[item.lower()] = item

            # 3. 提取可能的时间相关提醒
            time_reminders = self._extract_time_related_reminders(text)
            for item in time_reminders:
                if len(reminder_dict) < 5:  # 限制最多5个提醒
                    reminder_dict[item.lower()] = item

            # 转换为列表
            reminders = list(reminder_dict.values())

            # 限制提醒数量不超过4项
            if len(reminders) > 4:
                reminders = reminders[:4]

            # 缓存结果
            self._set_cache(cache_key, reminders, 3600)  # 缓存1小时

            logger.info(f"提取的关键点: {reminders}, 事件类型: {event_type}")
            return reminders

        except Exception as e:
            logger.exception(f"提取提醒时出错: {str(e)}")
            return []

    def _extract_reminders_with_ai(self, text: str, event_type: str) -> List[str]:
        """使用AI提取提醒

        Args:
            text: 原始文本
            event_type: 事件类型

        Returns:
            List[str]: AI提取的提醒列表
        """
        try:
            # 构建提示词
            prompt = f"""
请从以下文本中提取2-4个关键提醒点，这些提醒点应该是参加该活动需要准备或注意的事项。
这是一个{event_type}，请提取与此类活动相关的关键提醒。
只需返回提醒点列表，每行一个提醒点，不要包含任何解释或其他内容。
每个提醒点应该简短明了，不超过10个字符。

文本：{text}
"""
            # 调用AI API
            response = self._call_ai_api(prompt, temperature=0.3)

            # 处理响应
            reminders = []
            if isinstance(response, str):
                # 按行分割
                lines = response.strip().split('\n')
                for line in lines:
                    # 清理每一行
                    line = line.strip()
                    # 移除可能的序号、破折号等前缀
                    line = re.sub(r'^[\d\-\*\•\.\s]+', '', line).strip()
                    # 移除可能的引号
                    line = re.sub(r'^["\']+|["\']+$', '', line).strip()
                    if line and len(line) <= 15:  # 限制长度
                        reminders.append(line)
            else:
                # 如果是流式响应，收集完整内容
                full_response = ""
                for chunk in response:
                    full_response += chunk

                # 按行分割
                lines = full_response.strip().split('\n')
                for line in lines:
                    # 清理每一行
                    line = line.strip()
                    # 移除可能的序号、破折号等前缀
                    line = re.sub(r'^[\d\-\*\•\.\s]+', '', line).strip()
                    # 移除可能的引号
                    line = re.sub(r'^["\']+|["\']+$', '', line).strip()
                    if line and len(line) <= 15:  # 限制长度
                        reminders.append(line)

            # 限制数量
            if len(reminders) > 4:
                reminders = reminders[:4]

            return reminders

        except Exception as e:
            logger.warning(f"使用AI提取提醒时出错: {str(e)}")
            return []

    def _extract_explicit_reminders(self, text: str) -> List[str]:
        """提取文本中明确提到的提醒项

        Args:
            text: 原始文本

        Returns:
            List[str]: 提取的明确提醒列表
        """
        explicit_items = []

        # 预编译正则表达式以提高性能
        explicit_patterns = [
            (r'带上([^，。；！？,\.;!?]*)', 1),
            (r'携带([^，。；！？,\.;!?]*)', 1),
            (r'准备([^，。；！？,\.;!?]*)', 1),
            (r'记得([^，。；！？,\.;!?]*?带)', 1),
            (r'别忘了([^，。；！？,\.;!?]*)', 1),
            (r'需要([^，。；！？,\.;!?]*)', 1),
            (r'([^，。；！？,\.;!?]*?)是必需的', 1),
            (r'([^，。；！？,\.;!?]*?)很重要', 1),
            (r'确保([^，。；！？,\.;!?]*)', 1)
        ]

        for pattern, group_index in explicit_patterns:
            matches = re.finditer(pattern, text)
            for match in matches:
                item = match.group(group_index).strip()
                # 过滤无效项
                if item and len(item) >= 2 and item not in ['的', '了', '着', '和', '与']:
                    # 清理项目
                    item = re.sub(r'^[的了着和与]+|[的了着和与]+$', '', item).strip()
                    if item and len(item) >= 2:
                        explicit_items.append(item)

        return explicit_items

    def _extract_implicit_reminders_by_event_type(self, text: str, event_type: str) -> List[str]:
        """基于事件类型提取隐式提醒

        Args:
            text: 原始文本
            event_type: 事件类型

        Returns:
            List[str]: 提取的隐式提醒列表
        """
        implicit_items = []

        # 基于事件类型的关键词映射
        event_keywords = {
            '学习活动': {
                '教材': ['教材', '课本', '书', '讲义', '资料'],
                '电脑': ['笔记本电脑', '电脑', '笔记本', '平板', 'iPad', '电子设备'],
                '文具': ['笔', '笔记本', '纸', '文具', '钢笔', '铅笔', '橡皮'],
                '学生证': ['学生证', '校园卡', '身份证', '证件']
            },
            '考试活动': {
                '准考证': ['准考证', '考试证', '证件', '身份证'],
                '文具': ['笔', '橡皮', '尺子', '计算器', '文具盒'],
                '手表': ['手表', '时钟', '计时器'],
                '水': ['水', '水杯', '饮料']
            },
            '演讲活动': {
                '演讲稿': ['演讲稿', '讲稿', '发言稿', '稿子'],
                '电脑': ['电脑', '笔记本', 'PPT', '幻灯片'],
                '激光笔': ['激光笔', '指示器', '遥控器'],
                '水': ['水', '水杯', '饮料']
            },
            '会议活动': {
                '会议资料': ['会议资料', '文件', '资料', '文档'],
                '电脑': ['电脑', '笔记本', '平板', '记录设备'],
                '笔记本': ['笔记本', '笔', '记事本'],
                '工牌': ['工牌', '胸卡', '身份证', '证件']
            },
            '社交活动': {
                '礼物': ['礼物', '礼品', '伴手礼'],
                '钱包': ['钱包', '现金', '银行卡', '支付工具'],
                '手机': ['手机', '电话', '联系方式'],
                '着装': ['着装', '衣服', '服装', '正装']
            },
            '面试活动': {
                '简历': ['简历', '个人资料', '作品集'],
                '证件': ['身份证', '学历证', '证书', '证件'],
                '着装': ['正装', '西装', '着装', '衣服'],
                '笔': ['笔', '笔记本', '记事本']
            },
            '休闲活动': {
                '门票': ['门票', '入场券', '票'],
                '钱包': ['钱包', '现金', '银行卡', '支付工具'],
                '手机': ['手机', '相机', '拍照设备'],
                '水': ['水', '水杯', '饮料', '零食']
            },
            '运动活动': {
                '运动装备': ['运动鞋', '运动服', '装备', '衣服'],
                '水': ['水', '水壶', '饮料', '补给'],
                '毛巾': ['毛巾', '汗巾', '擦汗巾'],
                '会员卡': ['会员卡', '健身卡', '门禁卡']
            }
        }

        # 获取当前事件类型的关键词
        type_keywords = event_keywords.get(event_type, {})

        # 检查文本中是否包含这些关键词
        for category, keywords in type_keywords.items():
            for keyword in keywords:
                if keyword in text:
                    implicit_items.append(category)
                    break  # 每个类别只添加一次

        return implicit_items

    def _extract_time_related_reminders(self, text: str) -> List[str]:
        """提取时间相关的提醒

        Args:
            text: 原始文本

        Returns:
            List[str]: 提取的时间相关提醒
        """
        time_reminders = []

        # 检查是否有时间紧迫性
        urgency_patterns = [
            r'紧急', r'立即', r'马上', r'尽快', r'赶紧',
            r'不要迟到', r'准时', r'按时', r'不能晚'
        ]

        for pattern in urgency_patterns:
            if re.search(pattern, text):
                time_reminders.append('准时到达')
                break

        # 检查是否有提前准备的要求
        preparation_patterns = [
            r'提前(\d+)[分钟]', r'提前到达', r'早点到',
            r'早做准备', r'提前准备'
        ]

        for pattern in preparation_patterns:
            match = re.search(pattern, text)
            if match:
                if '分钟' in pattern and match.group(1):
                    time_reminders.append(f'提前{match.group(1)}分钟')
                else:
                    time_reminders.append('提前准备')
                break

        return time_reminders

    def _determine_event_type(self, text: str) -> str:
        """确定事件类型，用于更精确地提取关键词

        Args:
            text: 事件文本

        Returns:
            str: 事件类型
        """
        try:
            # 首先尝试使用缓存
            cache_key = f"event_type:{hashlib.md5(text.encode()).hexdigest()}"
            cached_result = self._get_cache(cache_key)
            if cached_result:
                logger.info("使用缓存的事件类型判断结果")
                return cached_result

            # 尝试使用AI判断事件类型（如果配置了AI API）
            if hasattr(settings, 'AI_API_KEY') and settings.AI_API_KEY and len(text) > 30:
                try:
                    ai_event_type = self._determine_event_type_with_ai(text)
                    if ai_event_type:
                        # 缓存结果
                        self._set_cache(cache_key, ai_event_type, 3600)  # 缓存1小时
                        return ai_event_type
                except Exception as e:
                    logger.warning(f"使用AI判断事件类型时出错: {str(e)}")

            # 使用更灵活的上下文分析而不是固定的关键词列表
            context_analysis = {
                '学习活动': {
                    '关键词': ['学习', '自习', '复习', '预习', '课程', '课堂', '上课', '听课', '授课', '教室', '图书馆',
                            '讲座', '讲课', '教学', '学校', '大学', '学院', '知识', '笔记', '作业', '练习'],
                    '权重': 1.0
                },
                '考试活动': {
                    '关键词': ['考试', '测验', '测试', '答题', '查核', '笔试', '口试', '考场', '试卷', '考核', '评估',
                            '考评', '成绩', '分数', '及格', '通过', '考官', '监考', '答卷'],
                    '权重': 1.2  # 考试通常更重要，给予更高权重
                },
                '演讲活动': {
                    '关键词': ['演讲', '汇报', '讲解', '发言', '宣讲', '演示', '展示', '报告', '讲座', '演说',
                            '讲演', '宣传', '宣讲', '宣传', '讲师', '主讲', '台上', '听众'],
                    '权重': 1.1
                },
                '会议活动': {
                    '关键词': ['会议', '讨论', '碰头', '沟通', '交流', '研讨', '座谈', '开会', '会商', '商讨',
                            '商议', '会场', '会谈', '洽谈', '协商', '商量', '议题', '议程', '主持'],
                    '权重': 1.0
                },
                '社交活动': {
                    '关键词': ['聚餐', '吃饭', '用餐', '聚会', '聚集', '食堂', '餐厅', '饭店', '聚会', '派对',
                            '社交', '交友', '朋友', '同学', '同事', '聚首', '相聚', '见面', '约会'],
                    '权重': 0.9
                },
                '面试活动': {
                    '关键词': ['面试', '招聘', '应聘', '求职', '笔试', '面谈', '答辩', '招聘会', '人力资源',
                            '简历', '岗位', '职位', '工作', '就业', '雇佣', '录用', 'HR', '人事'],
                    '权重': 1.2
                },
                '休闲活动': {
                    '关键词': ['休息', '放松', '娱乐', '游玩', '旅游', '观光', '游览', '参观', '游戏', '玩耍',
                            '度假', '休闲', '散步', '闲逛', '逛街', '购物', '电影', '音乐', '展览'],
                    '权重': 0.8
                },
                '运动活动': {
                    '关键词': ['运动', '锻炼', '健身', '跑步', '游泳', '打球', '健身房', '操场', '体育', '球场',
                            '训练', '比赛', '竞赛', '体能', '力量', '有氧', '无氧', '拉伸', '热身'],
                    '权重': 0.9
                }
            }

            # 计算各类型的匹配度
            scores = {}

            # 分析文本中的关键词出现情况
            for event_type, type_info in context_analysis.items():
                # 基础分数：关键词匹配数 * 权重
                base_score = sum(1 for keyword in type_info['关键词'] if keyword in text) * type_info['权重']

                # 上下文分析：检查关键词在文本中的位置和重要性
                # 标题或开头出现的关键词更重要
                text_parts = text.split('，')
                if len(text_parts) > 0:
                    first_part = text_parts[0]
                    first_part_bonus = sum(2 for keyword in type_info['关键词'] if keyword in first_part)
                    base_score += first_part_bonus

                # 检查关键词的密度
                if len(text) > 0:
                    keyword_density = sum(text.count(keyword) for keyword in type_info['关键词']) / len(text)
                    base_score += keyword_density * 10  # 调整权重

                # 检查特定的事件标识符
                event_identifiers = {
                    '学习活动': ['课', '学', '教', '读'],
                    '考试活动': ['考', '试', '测', '评'],
                    '演讲活动': ['讲', '演', '说', '台'],
                    '会议活动': ['会', '议', '商', '谈'],
                    '社交活动': ['聚', '餐', '友', '约'],
                    '面试活动': ['面', '试', '职', '聘'],
                    '休闲活动': ['玩', '游', '休', '闲'],
                    '运动活动': ['动', '练', '跑', '球']
                }

                identifiers = event_identifiers.get(event_type, [])
                identifier_score = sum(2 for identifier in identifiers if identifier in text)
                base_score += identifier_score

                # 记录最终分数
                scores[event_type] = base_score

            # 找出得分最高的类型
            max_score = 0
            best_type = '学习活动'  # 默认类型

            for event_type, score in scores.items():
                if score > max_score:
                    max_score = score
                    best_type = event_type

            logger.info(f"事件类型判断结果: {best_type}, 得分: {max_score}")

            # 如果最高分太低，可能无法确定类型，返回通用类型
            if max_score < 1.0:
                best_type = '通用活动'

            # 缓存结果
            self._set_cache(cache_key, best_type, 3600)  # 缓存1小时

            return best_type

        except Exception as e:
            logger.exception(f"确定事件类型时出错: {str(e)}")
            return '通用活动'

    def _determine_event_type_with_ai(self, text: str) -> str:
        """使用AI判断事件类型

        Args:
            text: 原始文本

        Returns:
            str: AI判断的事件类型
        """
        try:
            # 构建提示词
            prompt = f"""
请分析以下文本，判断它描述的是哪种类型的活动或事件。
请从以下选项中选择最匹配的一个：
1. 学习活动（如上课、自习、学习等）
2. 考试活动（如考试、测验等）
3. 演讲活动（如演讲、汇报、讲解等）
4. 会议活动（如会议、讨论、研讨等）
5. 社交活动（如聚餐、聚会等）
6. 面试活动（如面试、招聘等）
7. 休闲活动（如休息、娱乐、旅游等）
8. 运动活动（如运动、锻炼、健身等）
9. 通用活动（如果无法确定）

只需返回类型名称，不要包含任何解释或其他内容。例如：学习活动

文本：{text}
"""
            # 调用AI API
            response = self._call_ai_api(prompt, temperature=0.3)

            # 处理响应
            if isinstance(response, str):
                # 清理响应
                event_type = response.strip()
                # 移除可能的序号和多余空格
                event_type = re.sub(r'^\d+\.\s*', '', event_type).strip()
                # 移除可能的引号
                event_type = re.sub(r'^["\']+|["\']+$', '', event_type).strip()

                # 验证是否是有效的事件类型
                valid_types = [
                    '学习活动', '考试活动', '演讲活动', '会议活动',
                    '社交活动', '面试活动', '休闲活动', '运动活动', '通用活动'
                ]

                if event_type in valid_types:
                    return event_type
                else:
                    # 尝试模糊匹配
                    for valid_type in valid_types:
                        if valid_type in event_type or event_type in valid_type:
                            return valid_type

                    # 如果无法匹配，返回通用活动
                    return '通用活动'
            else:
                # 如果是流式响应，收集完整内容
                full_response = ""
                for chunk in response:
                    full_response += chunk

                # 清理响应
                event_type = full_response.strip()
                # 移除可能的序号和多余空格
                event_type = re.sub(r'^\d+\.\s*', '', event_type).strip()
                # 移除可能的引号
                event_type = re.sub(r'^["\']+|["\']+$', '', event_type).strip()

                # 验证是否是有效的事件类型
                valid_types = [
                    '学习活动', '考试活动', '演讲活动', '会议活动',
                    '社交活动', '面试活动', '休闲活动', '运动活动', '通用活动'
                ]

                if event_type in valid_types:
                    return event_type
                else:
                    # 尝试模糊匹配
                    for valid_type in valid_types:
                        if valid_type in event_type or event_type in valid_type:
                            return valid_type

                    # 如果无法匹配，返回通用活动
                    return '通用活动'

        except Exception as e:
            logger.warning(f"使用AI判断事件类型时出错: {str(e)}")
            return ""

    def _default_extract_reminders(self, text: str) -> List[str]:
        """默认的提醒提取方法，用作备份

        Args:
            text: 要提取提醒的文本

        Returns:
            List[str]: 提取的提醒列表
        """
        return self._extract_reminders(text)

    def _init_regex_patterns(self):
        """初始化常用的正则表达式模式"""
        # 日期相关的正则表达式
        self.date_patterns = {
            # 日期词映射
            'date_words': {
                '今天': 0,
                '明天': 1,
                '后天': 2,
                '大后天': 3,
                '昨天': -1,
                '前天': -2
            },
            # X天后格式
            'days_later': r'(\d+)[天日]后',
            # 完整日期格式 YYYY-MM-DD 或 YYYY年MM月DD日
            'full_date': r'(\d{4})[-年/](\d{1,2})[-月/](\d{1,2})[日号]?',
            # 月日格式
            'month_day': r'(\d{1,2})月(\d{1,2})[日号]',
            # 仅日期格式
            'day_only': r'(\d{1,2})[日号]',
            # 下个月X日
            'next_month': r'下(个)?月(\d{1,2})[日号]',
            # 本月X日
            'this_month': r'(这|本)(个)?月(\d{1,2})[日号]',
            # 下下周X格式
            'after_next_week': r'下下(个)?(周|星期|礼拜)([一二三四五六日天0-7])',
            # 下下下周X格式 - 新增
            'after_after_next_week': r'下下下(个)?(周|星期|礼拜)([一二三四五六日天0-7])',
            # 下周X格式
            'next_week': r'下(个)?(周|星期|礼拜)([一二三四五六日天0-7])',
            # 本周X/这周X格式
            'this_week': r'(本|这)(个)?(周|星期|礼拜)([一二三四五六日天0-7])',
            # 星期X或礼拜X格式
            'weekday_full': r'(星期|礼拜)([一二三四五六日天0-7])',
            # 支持"下个月第X个周Y"格式 - 新增
            'next_month_nth_weekday': r'下(个)?月第(\d+)(个)?(周|星期|礼拜)([一二三四五六日天0-7])',
            # 支持"本月第X个周Y"格式 - 新增
            'this_month_nth_weekday': r'(本|这)(个)?月第(\d+)(个)?(周|星期|礼拜)([一二三四五六日天0-7])',
            # 周几格式 - 修复为更精确的模式
            'weekday': r'(?:周|星期|礼拜)([一二三四五六日天0-7])',
            # 周几范围格式
            'weekday_range': r'(?:周|星期|礼拜)([一二三四五六日天])(?:[到至\-~])(?:周|星期|礼拜)?([一二三四五六日天])',
            # 周几括号范围格式
            'weekday_range_paren': r'(?:周|星期|礼拜)[（(]([一二三四五六日天])(?:[到至\-~])([一二三四五六日天])[）)]',
            # 不带前缀的周几表达，如"周一"、"周二"等 - 完全重写这个正则表达式
            'weekday_only': r'(?<!(下|下下|下下下|本|这)(个)?(周|星期|礼拜))(?:周|星期|礼拜)([一二三四五六日天0-7])',
            # 周末表达
            'weekend': r'(?:本|这|下|下下)?(?:个)?(?:周|星期)末',
            # 支持斜杠和横杠分隔的日期
            'slash_date': r'(\d{1,2})[/\-](\d{1,2})',
            # 支持"X号"格式
            'day_with_hao': r'(\d{1,2})号'
        }

        # 时间相关的正则表达式
        self.time_patterns = {
            # 24小时制时间
            'time_24h': r'(\d{1,2})[点:：时](\d{1,2})?(?:分)?',
            # 带时段的时间
            'time_period': r'([早中下晚])上?(\d{1,2})[点:：时](\d{1,2})?(?:分)?',
            # 时间范围
            'time_range': r'(\d{1,2})[点:：时](\d{1,2})?(?:分)?[到至\-~](\d{1,2})[点:：时](\d{1,2})?(?:分)?'
        }

        # 时间段相关的映射
        self.period_map = {
            '早': 'morning',
            '上': 'morning',
            '早上': 'morning',
            '上午': 'morning',
            '中': 'noon',
            '中午': 'noon',
            '下': 'afternoon',
            '下午': 'afternoon',
            '晚': 'evening',
            '晚上': 'evening'
        }

        # 字符清理相关的正则表达式
        self.cleanup_patterns = {
            # 移除常见的提醒前缀和噪音
            'noise': [
                r'(记得|别忘了|需要|带上|携带).*?[，。；！？,\.;!?]',
                r'请.*?[，。；！？,\.;!?]',
                r'要.*?[，。；！？,\.;!?]'
            ],
            # 删除描述开头的常见介词和连词
            'prefix': r'^(在|于|和|跟|与|同|给|对|为|从|向|把|被|让|由)',
            # 删除末尾的标点符号
            'punctuation': r'[，。；！？,.;!?]$'
        }

        # 关键词相关的列表
        self.keywords = {
            # 动作关键词
            'action': ['参加', '考试', '讨论', '自习', '准备', '开会', '复习', '学习',
                     '汇报', '演讲', '聚餐', '会面', '安排', '完成', '提交', '面试'],
            # 事件关键词
            'event': [
                # 学习相关
                (r'(考试|测验|quiz|考核|测试)', "考试"),
                (r'(复习|预习|准备|练习)', "复习"),
                (r'(自习|学习|做题|刷题)', "学习"),
                (r'(作业|作品|论文|报告|展示)', "作业"),
                (r'(演讲|汇报|讲解|发言)', "演讲"),
                # 活动相关
                (r'(讨论|会议|开会|碰头|沟通)', "讨论"),
                (r'(项目|课题|设计|开发|研究)', "项目"),
                (r'(聚餐|吃饭|聚会|集合)', "聚餐"),
                (r'(面试|答辩|评审|审核)', "面试")
            ]
        }

        # 提醒相关的正则表达式
        self.reminder_patterns = [
            r'记得([^，。；！？,\.;!?]*)',
            r'别忘了([^，。；！？,\.;!?]*)',
            r'需要([^，。；！？,\.;!?]*)',
            r'准备([^，。；！？,\.;!?]*)',
            r'带上([^，。；！？,\.;!?]*)',
            r'携带([^，。；！？,\.;!?]*)'
        ]

        # 周几映射表优化
        self.weekday_map = {
            '一': 0, '二': 1, '三': 2, '四': 3, '五': 4, '六': 5, '日': 6,
            '天': 6, '七': 6,  # "周天"和"周七"都表示周日
            '1': 0, '2': 1, '3': 2, '4': 3, '5': 4, '6': 5, '7': 6, '0': 0,
            'mon': 0, 'tue': 1, 'wed': 2, 'thu': 3, 'fri': 4, 'sat': 5, 'sun': 6,
            'monday': 0, 'tuesday': 1, 'wednesday': 2, 'thursday': 3,
            'friday': 4, 'saturday': 5, 'sunday': 6,
            '星期一': 0, '星期二': 1, '星期三': 2, '星期四': 3,
            '星期五': 4, '星期六': 5, '星期日': 6, '星期天': 6,
            '礼拜一': 0, '礼拜二': 1, '礼拜三': 2, '礼拜四': 3,
            '礼拜五': 4, '礼拜六': 5, '礼拜日': 6, '礼拜天': 6,
            '周一': 0, '周二': 1, '周三': 2, '周四': 3,
            '周五': 4, '周六': 5, '周日': 6, '周天': 6,
            # 新增更多映射
            '工作日': [0,1,2,3,4],  # 周一到周五
            '平日': [0,1,2,3,4],    # 周一到周五
            '周末': [5,6],          # 周六和周日
            '休息日': [5,6],        # 周六和周日
        }

        logger.info("正则表达式模式初始化完成")

    def _extract_keywords(self, text: str, max_keywords: int = 10) -> List[str]:
        """提取文本关键词

        Args:
            text (str): 需要提取关键词的文本
            max_keywords (int, optional): 最大关键词数量. 默认为 10

        Returns:
            List[str]: 关键词列表
        """
        try:
            # 如果文本为空或太短，直接返回空列表
            if not text or len(text) < 5:
                return []

            # 使用jieba分词提取关键词（备选方案）
            try:
                import jieba.analyse
                # 使用TF-IDF算法提取关键词
                jieba_keywords = jieba.analyse.extract_tags(text, topK=max_keywords)
                if jieba_keywords:
                    return jieba_keywords
            except Exception as e:
                logger.warning(f"使用jieba提取关键词失败: {str(e)}")

            # 使用AI提取关键词
            messages = [
                {
                    'role': 'system',
                    'content': f"""你是一个专业的文本分析助手。请从以下文本中提取最多{max_keywords}个关键词，
                    以JSON数组格式返回，格式如下：
                    ["关键词1", "关键词2", "关键词3"]
                    
                    要求：
                    1. 关键词应该反映文本的主要内容和主题
                    2. 每个关键词长度不超过10个字符
                    3. 按重要性排序
                    4. 不要包含重复或相似的关键词"""
                },
                {
                    'role': 'user',
                    'content': text
                }
            ]

            response = self._call_ai_api(messages, temperature=0.3)

            # 处理流式响应
            if hasattr(response, '__iter__') and not isinstance(response, str):
                response_text = ""
                for chunk in response:
                    response_text += chunk
                response = response_text

            # 清理响应文本
            response = response.strip()
            if response.startswith('```json'):
                response = response[7:]
            if response.endswith('```'):
                response = response[:-3]
            response = response.strip()

            try:
                keywords = json.loads(response)
                if isinstance(keywords, list):
                    # 确保返回的是字符串列表
                    keywords = [str(k).strip() for k in keywords if k]
                    # 去重
                    keywords = list(dict.fromkeys(keywords))
                    # 限制数量
                    return keywords[:max_keywords]
                else:
                    logger.warning(f"关键词提取返回了非列表结果: {response}")
                    return []

            except json.JSONDecodeError as e:
                logger.error(f"关键词JSON解析失败: {str(e)}, 原始响应: {response}")

                # 尝试从文本中提取关键词
                try:
                    # 使用正则表达式提取可能的JSON数组
                    import re
                    json_array_match = re.search(r'\[(.*?)\]', response)
                    if json_array_match:
                        json_array = json_array_match.group(0)
                        keywords = json.loads(json_array)
                        if isinstance(keywords, list):
                            keywords = [str(k).strip() for k in keywords if k]
                            keywords = list(dict.fromkeys(keywords))
                            return keywords[:max_keywords]
                except Exception:
                    pass

                # 如果所有方法都失败，返回空列表
                return []

        except Exception as e:
            logger.error(f"提取关键词失败: {str(e)}")
            return []

    def _parse_time(self, text: str) -> Tuple[str, str, bool]:
        """解析时间信息

        Args:
            text: 包含时间信息的文本

        Returns:
            Tuple[str, str, bool]: (开始时间, 结束时间, 是否包含结束时间)
        """
        try:
            # 1. 预处理文本
            text = text.strip()

            # 2. 尝试匹配标准时间格式
            time_patterns = [
                r'(\d{1,2}[:：]\d{2})\s*[-到至]\s*(\d{1,2}[:：]\d{2})',  # 10:00-12:00
                r'(\d{1,2}[:：]\d{2})\s*到\s*(\d{1,2}[:：]\d{2})',      # 10:00到12:00
                r'(\d{1,2}[:：]\d{2})\s*[-到至]\s*(\d{1,2})',           # 10:00-12
                r'(\d{1,2}[:：]\d{2})\s*到\s*(\d{1,2})',                # 10:00到12
            ]

            for pattern in time_patterns:
                match = re.search(pattern, text)
                if match:
                    start_time = match.group(1)
                    end_time = match.group(2)
                    # 标准化时间格式
                    start_time = self._standardize_time(start_time)
                    end_time = self._standardize_time(end_time)
                    return start_time, end_time, True

            # 3. 尝试匹配单个时间点
            single_time_patterns = [
                r'(\d{1,2}[:：]\d{2})',  # 10:00
                r'(\d{1,2})点',           # 10点
                r'(\d{1,2})时',           # 10时
            ]

            for pattern in single_time_patterns:
                match = re.search(pattern, text)
                if match:
                    time = match.group(1)
                    # 标准化时间格式
                    time = self._standardize_time(time)
                    # 根据上下文推断持续时间
                    duration = self._infer_duration(text)
                    end_time = self._calculate_end_time(time, duration)
                    return time, end_time, True

            # 4. 尝试匹配模糊时间表达
            fuzzy_time_patterns = {
                r'早上|上午': '09:00',
                r'中午': '12:00',
                r'下午': '14:00',
                r'晚上': '19:00',
                r'凌晨': '00:00',
                r'课间': '10:00',
                r'午休': '12:30',
                r'傍晚': '18:00',
            }

            for pattern, default_time in fuzzy_time_patterns.items():
                if re.search(pattern, text):
                    duration = self._infer_duration(text)
                    end_time = self._calculate_end_time(default_time, duration)
                    return default_time, end_time, True

            # 5. 默认时间
            default_time = "09:00"
            duration = self._infer_duration(text)
            end_time = self._calculate_end_time(default_time, duration)
            return default_time, end_time, True

        except Exception as e:
            logger.warning(f"解析时间时出错: {str(e)}")
            return "09:00", "10:00", True

    def _standardize_time(self, time: str) -> str:
        """标准化时间格式"""
        try:
            # 移除所有非数字字符
            time = re.sub(r'[^\d]', '', time)

            # 确保时间格式为HH:MM
            if len(time) == 4:
                time = f"{time[:2]}:{time[2:]}"
            elif len(time) == 2:
                time = f"{time}:00"
            else:
                time = "09:00"  # 默认时间

            return time

        except Exception as e:
            logger.warning(f"标准化时间格式时出错: {str(e)}")
            return "09:00"

    def _infer_duration(self, text: str) -> int:
        """根据文本推断持续时间（分钟）"""
        try:
            # 1. 检查是否有明确的持续时间
            duration_patterns = {
                r'(\d+)小时': lambda x: int(x) * 60,
                r'(\d+)分钟': lambda x: int(x),
                r'(\d+)个钟头': lambda x: int(x) * 60,
            }

            for pattern, converter in duration_patterns.items():
                match = re.search(pattern, text)
                if match:
                    return converter(match.group(1))

            # 2. 根据活动类型推断
            activity_durations = {
                r'会议|开会': 60,
                r'课程|上课': 45,
                r'考试': 120,
                r'讨论': 30,
                r'演讲': 45,
                r'面试': 30,
                r'聚餐': 90,
                r'自习': 120,
            }

            for pattern, duration in activity_durations.items():
                if re.search(pattern, text):
                    return duration

            # 3. 默认持续时间
            return 60  # 默认1小时

        except Exception as e:
            logger.warning(f"推断持续时间时出错: {str(e)}")
            return 60

    def _calculate_end_time(self, start_time: str, duration: int) -> str:
        """计算结束时间"""
        try:
            # 解析开始时间
            hours, minutes = map(int, start_time.split(':'))

            # 计算结束时间
            total_minutes = hours * 60 + minutes + duration
            end_hours = total_minutes // 60
            end_minutes = total_minutes % 60

            # 格式化结束时间
            return f"{end_hours:02d}:{end_minutes:02d}"

        except Exception as e:
            logger.warning(f"计算结束时间时出错: {str(e)}")
            return "10:00"

    def _extract_location(self, text: str) -> Tuple[str, bool]:
        """提取地点信息

        Args:
            text: 包含地点信息的文本

        Returns:
            Tuple[str, bool]: (地点, 是否为AI推断)
        """
        try:
            # 1. 预处理文本
            text = text.strip()

            # 2. 尝试匹配标准地点格式
            location_patterns = [
                r'地点[：:]\s*([^，。；\n]+)',  # 地点：xxx
                r'在\s*([^，。；\n]+)',        # 在xxx
                r'于\s*([^，。；\n]+)',        # 于xxx
                r'到\s*([^，。；\n]+)',        # 到xxx
                r'地点\s*([^，。；\n]+)',      # 地点xxx
            ]

            for pattern in location_patterns:
                match = re.search(pattern, text)
                if match:
                    location = match.group(1).strip()
                    # 清理地点文本
                    location = self._clean_location(location)
                    if location:
                        return location, False

            # 3. 尝试匹配教室格式
            classroom_patterns = [
                r'(\d+)[-]?(\d+)\s*教室',     # 101教室
                r'(\d+)[-]?(\d+)\s*号教室',   # 101号教室
                r'(\d+)[-]?(\d+)\s*室',       # 101室
                r'(\d+)[-]?(\d+)\s*号',       # 101号
            ]

            for pattern in classroom_patterns:
                match = re.search(pattern, text)
                if match:
                    building = match.group(1)
                    room = match.group(2)
                    location = f"{building}-{room}教室"
                    return location, False

            # 4. 尝试匹配常见地点
            common_locations = {
                r'教室': '教室',
                r'会议室': '会议室',
                r'办公室': '办公室',
                r'实验室': '实验室',
                r'图书馆': '图书馆',
                r'食堂': '食堂',
                r'操场': '操场',
                r'礼堂': '礼堂',
                r'报告厅': '报告厅',
                r'演播厅': '演播厅',
                r'体育馆': '体育馆',
                r'活动中心': '活动中心',
                r'教学楼': '教学楼',
                r'行政楼': '行政楼',
                r'宿舍': '宿舍',
            }

            for pattern, location in common_locations.items():
                if re.search(pattern, text):
                    return location, False

            # 5. 尝试从上下文推断
            inferred_location = self._infer_location_from_context(text)
            if inferred_location:
                return inferred_location, True

            # 6. 默认地点
            return "未指定地点", True

        except Exception as e:
            logger.warning(f"提取地点时出错: {str(e)}")
            return "未指定地点", True

    def _clean_location(self, location: str) -> str:
        """清理地点文本"""
        try:
            # 1. 移除多余空格
            location = re.sub(r'\s+', ' ', location)

            # 2. 移除标点符号
            location = re.sub(r'[，。；]', '', location)

            # 3. 移除常见前缀
            prefixes = ['在', '于', '到', '地点', '位置']
            for prefix in prefixes:
                if location.startswith(prefix):
                    location = location[len(prefix):]

            # 4. 移除常见后缀
            suffixes = ['进行', '举行', '开展', '举办']
            for suffix in suffixes:
                if location.endswith(suffix):
                    location = location[:-len(suffix)]

            return location.strip()

        except Exception as e:
            logger.warning(f"清理地点文本时出错: {str(e)}")
            return location

    def _infer_location_from_context(self, text: str) -> Optional[str]:
        """从上下文推断地点"""
        try:
            # 1. 检查是否有时间信息
            time_match = re.search(r'\d{1,2}[:：]\d{2}', text)
            if time_match:
                # 根据时间推断可能的地点
                time = time_match.group(0)
                hour = int(time.split(':')[0])

                if 7 <= hour <= 8:
                    return "教室"  # 早上可能是上课
                elif 12 <= hour <= 13:
                    return "食堂"  # 中午可能是吃饭
                elif 14 <= hour <= 17:
                    return "教室"  # 下午可能是上课
                elif 19 <= hour <= 21:
                    return "教室"  # 晚上可能是自习

            # 2. 检查是否有活动类型
            activity_locations = {
                r'上课|课程': '教室',
                r'开会|会议': '会议室',
                r'考试': '教室',
                r'实验': '实验室',
                r'运动|体育': '操场',
                r'演讲|报告': '报告厅',
                r'演出|表演': '演播厅',
                r'吃饭|用餐': '食堂',
                r'自习': '教室',
                r'讨论': '会议室',
            }

            for pattern, location in activity_locations.items():
                if re.search(pattern, text):
                    return location

            return None

        except Exception as e:
            logger.warning(f"从上下文推断地点时出错: {str(e)}")
            return None

    def _determine_importance(self, text: str) -> str:
        """判断日程重要性

        Args:
            text: 包含重要性信息的文本

        Returns:
            str: 重要性级别（high/medium/low）
        """
        try:
            # 1. 预处理文本
            text = text.strip()

            # 2. 检查明确的重要性标记
            importance_markers = {
                'high': [
                    r'重要|紧急|必须|务必|一定要|关键|核心|优先|首要|重点',
                    r'考试|面试|答辩|汇报|演讲|答辩|答辩会',
                    r'截止|deadline|ddl|最后期限',
                    r'会议|开会|讨论|研讨',
                    r'项目|任务|工作|作业',
                ],
                'medium': [
                    r'一般|普通|常规|例行|日常',
                    r'课程|上课|讲座|培训',
                    r'活动|聚会|聚餐',
                    r'练习|训练|锻炼',
                    r'复习|预习|自习',
                ],
                'low': [
                    r'可选|随意|有空|方便|合适',
                    r'娱乐|休闲|放松|休息',
                    r'聊天|交流|沟通',
                    r'参观|游览|游玩',
                    r'购物|逛街|买东西',
                ]
            }

            # 计算每个级别的重要性得分
            scores = {'high': 0, 'medium': 0, 'low': 0}

            for level, patterns in importance_markers.items():
                for pattern in patterns:
                    if re.search(pattern, text):
                        scores[level] += 1

            # 3. 检查时间紧迫性
            time_patterns = {
                r'今天|明天|后天|本周|下周': 2,  # 近期
                r'下周|下下周|月底|下月': 1,    # 中期
                r'下个月|下下个月|年底|明年': 0  # 远期
            }

            time_score = 0
            for pattern, score in time_patterns.items():
                if re.search(pattern, text):
                    time_score = score
                    break

            # 4. 检查是否有明确的时间点
            if re.search(r'\d{1,2}[:：]\d{2}', text):
                time_score += 1

            # 5. 检查是否有地点信息
            if re.search(r'地点|在|于|到', text):
                time_score += 1

            # 6. 计算最终得分
            final_scores = {
                'high': scores['high'] * 3 + time_score,
                'medium': scores['medium'] * 2 + time_score,
                'low': scores['low'] + time_score
            }

            # 7. 确定重要性级别
            max_score = max(final_scores.values())
            if max_score == 0:
                return 'medium'  # 默认中等重要性

            for level, score in final_scores.items():
                if score == max_score:
                    return level

            return 'medium'  # 默认中等重要性

        except Exception as e:
            logger.warning(f"判断重要性时出错: {str(e)}")
            return 'medium'  # 出错时返回中等重要性

    def _calculate_weekday_date(self, current_date, target_weekday, is_next_week=False, is_this_week=False, is_after_next_week=False):
        """计算目标周几的具体日期
        
        Args:
            current_date (date): 当前日期
            target_weekday (int): 目标周几（0-6，0=周一）
            is_next_week (bool): 是否明确指定下周
            is_this_week (bool): 是否明确指定本周
            is_after_next_week (bool): 是否明确指定下下周
        
        Returns:
            date: 计算得到的日期
        """
        current_weekday = current_date.weekday()
        current_time = datetime.now().time()
        
        # 记录初始参数
        logger.info(f"计算周几日期 - 当前日期:{current_date}, 当前周几:{current_weekday}, 目标周几:{target_weekday}")
        logger.info(f"指定周次 - 本周:{is_this_week}, 下周:{is_next_week}, 下下周:{is_after_next_week}")

        # 1. 处理明确指定的情况
        if is_after_next_week:
            # 下下周：基准是14天后，然后调整到目标周几
            days_until = 14 + (target_weekday - current_weekday)
            if days_until >= 21:  # 如果超过三周，回退一周
                days_until -= 7
            logger.info(f"下下周 - 计算天数:{days_until}")
            return current_date + timedelta(days=days_until)
            
        if is_next_week:
            # 下周：基准是7天后，然后调整到目标周几
            days_until = 7 + (target_weekday - current_weekday)
            if days_until >= 14:  # 如果超过两周，回退一周
                days_until -= 7
            logger.info(f"下周 - 计算天数:{days_until}")
            return current_date + timedelta(days=days_until)
            
        if is_this_week:
            # 本周：如果目标日已过，转到下周
            if target_weekday < current_weekday or (target_weekday == current_weekday and current_time.hour >= 18):
                days_until = 7 + (target_weekday - current_weekday)  # 转到下周
                logger.info(f"本周已过 - 转到下周，计算天数:{days_until}")
            else:
                days_until = target_weekday - current_weekday
                logger.info(f"本周 - 计算天数:{days_until}")
            return current_date + timedelta(days=days_until)

        # 2. 处理未明确指定周次的情况（智能判断）
        # 修复：当用户只说"周五"而不是"本周五"时，应该默认为最近的未来周五
        
        days_until = target_weekday - current_weekday
        
        # 关键修复：用户说"周五"等无明确周次时，当目标日期小于当前日期，自动加7天
        if days_until < 0:
            days_until += 7
            logger.info(f"目标周几在本周已过，自动调整为下周，计算天数:{days_until}")
        
        # 2.1 处理同一天的情况
        if days_until == 0:
            # 晚上6点后，自动安排到下周
            if current_time.hour >= 18:
                days_until = 7
                logger.info("当天晚上6点后 - 自动安排到下周")
            # 中午12点到晚上6点之间
            elif current_time.hour >= 12:
                # 工作日自动安排到下周，周末保持当天
                if current_weekday < 5:  # 周一到周五
                    days_until = 7
                    logger.info("工作日下午 - 自动安排到下周")
                else:
                    logger.info("周末 - 保持当天安排")
            else:
                logger.info("当天上午 - 保持当天安排")
                
        # 2.3 特殊处理周末情况
        if target_weekday >= 5:  # 目标是周六或周日
            if current_weekday < 5:  # 当前是工作日
                # 目标是本周末，保持原样
                logger.info("当前是工作日，目标是本周末 - 保持原样")
            elif days_until == 0 and current_time.hour >= 12:
                # 当天是周末且已经过了中午，转到下周
                days_until = 7
                logger.info("当天是周末且过了中午 - 转到下周")
            elif days_until > 0 and days_until < 3:
                # 如果目标周末与当前相差不到3天，保持原样
                logger.info("目标周末与当前相差不到3天 - 保持原样")
                
        # 3. 最终检查
        # 确保计算结果不会超出三周
        if days_until >= 21:
            days_until = days_until % 7
            logger.warning(f"计算天数超出三周，修正为:{days_until}")
        
        # 确保不会安排在过去的时间
        if days_until == 0 and current_time.hour >= 23:
            days_until = 7
            logger.info("当天即将结束 - 自动安排到下周")
            
        target_date = current_date + timedelta(days=days_until)
        logger.info(f"最终计算结果 - 相差天数:{days_until}, 目标日期:{target_date}")
        return target_date

    def _extract_date_with_context(self, text, current_date, event_context=None):
        """根据文本上下文提取日期信息

        Args:
            text: 包含日期信息的文本
            current_date: 当前日期参考点
            event_context: 事件上下文

        Returns:
            date: 提取的日期，如果无法提取则返回None
        """
        try:
            extracted_date = self._extract_date_info(text, current_date)
            return extracted_date
        except Exception as e:
            logger.error(f"根据上下文提取日期失败: {str(e)}")
            return current_date

    def _validate_and_correct_date(self, extracted_date: date, text: str, current_date: date) -> date:
        """验证并修正提取的日期

        修正日期是非常重要的，因为可能出现非法日期
        例如：2月30日不存在，需要修正为2月28日/29日
        9月31日不存在，需要修正为9月30日

        Args:
            extracted_date: 提取的日期
            text: 原始文本
            current_date: 当前参考日期

        Returns:
            date: 修正后的日期
        """
        try:
            # 首先检查提取的日期是否为空
            if not extracted_date:
                logger.warning("提取的日期为空，返回当前日期")
                return current_date

            # 检查日期是否有效 - 有些日期可能无效（如2月30日）
            year = extracted_date.year
            month = extracted_date.month
            day = extracted_date.day

            # 检查月份是否有效
            if month < 1 or month > 12:
                logger.warning(f"无效的月份: {month}，修正为当前月份")
                month = current_date.month

            # 检查日期是否有效 - 不同月份的天数不同
            days_in_month = {
                1: 31, 2: 28, 3: 31, 4: 30, 5: 31, 6: 30,
                7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31
            }

            # 处理闰年二月
            if month == 2:
                if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
                    days_in_month[2] = 29
                    logger.info(f"{year}年是闰年，二月有29天")

            # 如果日期超出当月天数，修正为当月最后一天
            if day < 1 or day > days_in_month[month]:
                logger.warning(f"无效的日期: {year}-{month}-{day}，日期超出范围，修正为当月最后一天")
                day = days_in_month[month]

            # 处理月份边界问题 - 特别是12月到1月的跨年问题
            # 已经在上面的代码中正确处理

            # 构建修正后的日期
            corrected_date = date(year, month, day)

            # 检查修正后的日期是否合理
            # 如果日期是过去的日期，且文本中没有明确指定过去的日期，则可能需要调整
            if corrected_date < current_date and not self._has_explicit_past_date(text):
                # 检查是否是近期日期（30天内）
                days_diff = (current_date - corrected_date).days
                if days_diff <= 30:
                    # 可能是用户想要设置未来的日期，但没有明确指定年份
                    # 例如，现在是2023年12月25日，用户说"1月5日"，应该是指2024年1月5日
                    if corrected_date.month < current_date.month:
                        # 月份小于当前月份，可能是指明年
                        new_date = date(year + 1, month, day)
                        logger.info(f"日期 {corrected_date} 可能是指明年，修正为 {new_date}")
                        corrected_date = new_date
                    elif corrected_date.month == current_date.month and corrected_date.day < current_date.day:
                        # 同月但日期小于当前日期，可能是指明年
                        new_date = date(year + 1, month, day)
                        logger.info(f"日期 {corrected_date} 可能是指明年，修正为 {new_date}")
                        corrected_date = new_date
                else:
                    # 日期差距较大，可能是用户确实想要设置过去的日期
                    # 或者是日期提取错误，此时保持原样
                    logger.info(f"日期 {corrected_date} 与当前日期相差 {days_diff} 天，保持原样")

            # 检查日期是否太远（超过一年）
            one_year_later = current_date + timedelta(days=365)
            if corrected_date > one_year_later:
                # 检查是否是因为年份错误导致的
                if not self._has_explicit_year(text) and corrected_date.replace(year=current_date.year) <= one_year_later:
                    # 如果没有明确指定年份，且将年份改为当前年份后日期合理，则使用当前年份
                    new_date = corrected_date.replace(year=current_date.year)
                    logger.info(f"日期 {corrected_date} 太远，修正为当前年份: {new_date}")
                    corrected_date = new_date
                else:
                    # 否则保持原样，可能用户确实想要设置远期日期
                    logger.info(f"日期 {corrected_date} 超过一年，但保持原样")

            logger.info(f"验证后的日期: {corrected_date}")
            return corrected_date
        except Exception as e:
            logger.error(f"验证日期时出错: {str(e)}")
            return current_date

    def _has_explicit_past_date(self, text: str) -> bool:
        """检查文本中是否明确指定了过去的日期

        Args:
            text: 要检查的文本

        Returns:
            bool: 如果文本中明确指定了过去的日期，返回True；否则返回False
        """
        # 检查是否包含明确表示过去的词语
        past_keywords = ['上个月', '上周', '前天', '昨天', '过去', '之前', '上个']
        for keyword in past_keywords:
            if keyword in text:
                return True

        # 检查是否包含"去年"等明确的年份表示
        if '去年' in text or re.search(r'20[0-1]\d年', text):  # 匹配2000-2019年
            return True

        return False